├── README ├── autoload ├── visualrepeat.vim └── visualrepeat │ └── reapply.vim ├── doc └── visualrepeat.txt └── plugin └── visualrepeat.vim /README: -------------------------------------------------------------------------------- 1 | This is a mirror of http://www.vim.org/scripts/script.php?script_id=3848 2 | 3 | DESCRIPTION 4 | This plugin defines repetition of Vim built-in normal mode commands via . 5 | for visual mode. Additionally, it offers functions like the popular repeat.vim 6 | plugin that allow mappings to be repeated in visual mode, too. 7 | 8 | VISUAL MODE REPETITION 9 | This extends the built-in normal mode repeat . to visual mode. 10 | 11 | VISUAL MODE MAPPING REPETITION 12 | Like with repeat.vim for normal mode, visual mode mappings can register a 13 | mapping to be used for visual mode repetition. 14 | 15 | Likewise, normal mode mappings can (in addition to the repeat.vim registration 16 | of a normal mode mapping) register a visual mode mapping with visualrepeat.vim 17 | that will be repeated in visual mode. 18 | 19 | Operator-pending mappings end with g@ and repeat naturally; i.e. Vim 20 | re-applies the 'opfunc' on the equivalent text (but at the current cursor 21 | position). But without a call to repeat#set(), it is impossible to repeat this 22 | operator-pending mapping to the current visual selection. Plugins cannot call 23 | repeat#set() in their operator-pending mapping, because then Vim's built-in 24 | repeat would be circumvented, the full mapping ending with g@ would be 25 | re-executed, and the repetition would then wait for the {motion}, what is not 26 | wanted. 27 | Therefore, this plugin offers a separate visualrepeat#set() function that can 28 | be invoked for operator-pending mappings. It can also be invoked for 29 | normal-mode mappings that have already called repeat#set(), and may override 30 | that mapping with a special repeat mapping for visual mode repeats. Together 31 | with the remapped {Visual}. command, this allows repetition - similar to what 32 | the built-in Vim commands do - across normal, operator-pending and visual 33 | mode. 34 | 35 | SOURCE 36 | - Based on vimtip #1142, Repeat last command and put cursor at start of change 37 | http://vim.wikia.com/wiki/Repeat_last_command_and_put_cursor_at_start_of_change 38 | - The client interface and implementation has been based on repeat.vim 39 | (vimscript #2136) by Tim Pope. 40 | 41 | RELATED WORKS 42 | - repeat.vim (vimscript #2136) has been the basis for this plugin and should 43 | be used in conjunction with visualrepeat.vim. (Otherwise, you'd have visual 44 | mode repeat, but no repeat in normal mode.) 45 | 46 | USAGE 47 | {Visual}. Repeat last change in all visually selected lines. 48 | - characterwise: Start from cursor position. 49 | - linewise: Each line separately, starting from the 50 | current column (usually the first in this mode). 51 | - blockwise: Only the selected text. This is 52 | implemented by temporarily duplicating the selection 53 | to separate lines and repeating over those, starting 54 | from the first column. 55 | 56 | Note: If the last normal mode command included a 57 | {motion} (e.g. g~e), the repetition will also move 58 | exactly over this {motion}, NOT the visual selection! 59 | It is thus best to repeat commands that work on the 60 | entire line (e.g. g~$). 61 | -------------------------------------------------------------------------------- /autoload/visualrepeat.vim: -------------------------------------------------------------------------------- 1 | " visualrepeat.vim: Repeat command extended to visual mode. 2 | " 3 | " DEPENDENCIES: 4 | " - ingo/selection.vim autoload script (optional; for blockwise repeat only) 5 | " 6 | " Copyright: (C) 2011-2013 Ingo Karkat 7 | " The VIM LICENSE applies to this script; see ':help copyright'. 8 | " 9 | " Maintainer: Ingo Karkat 10 | " 11 | " REVISION DATE REMARKS 12 | " 1.30.014 14-Nov-2013 ENH: When repeating over multiple lines / a 13 | " blockwise selection, keep track of added or 14 | " deleted lines, and only repeat exactly on the 15 | " selected lines. Thanks to Israel Chauca for 16 | " sending a patch! 17 | " When a repeat on a blockwise selection has 18 | " introduced additional lines, append those 19 | " properly indented instead of omitting them. 20 | " With linewise and blockwise repeat, set the 21 | " change marks '[,'] to the changed selection. 22 | " With the latter, one previously got "E19: 23 | " Mark has invalid line number" due to the removed 24 | " temporary range. 25 | " 1.20.013 05-Sep-2013 ENH: Implement blockwise repeat through 26 | " temporarily moving the block to a temporary 27 | " range at the end of the buffer, like the vis.vim 28 | " plugin. 29 | " 1.10.012 04-Sep-2013 ENH: Use the current cursor virtual column when 30 | " repeating in linewise visual mode. Add 31 | " visualrepeat#CaptureVirtCol() and 32 | " visualrepeat#repeatOnVirtCol() for that. 33 | " Minor: Also catch Vim echoerr exceptions and 34 | " anything else. 35 | " Move the error handling to the mapping itself 36 | " and do it with echoerr so that further commands 37 | " are properly aborted. Implement 38 | " visualrepeat#ErrorMsg() to avoid a dependency to 39 | " ingo#err#Get(). 40 | " 1.10.011 14-Jun-2013 Minor: Make substitute() robust against 41 | " 'ignorecase'. 42 | " 1.10.010 18-Apr-2013 Check for existence of actual visual mode 43 | " mapping; do not accept a select mode mapping, 44 | " because we're applying it to a visual selection. 45 | " Pass through a [count] to the :normal . command. 46 | " 1.03.009 21-Feb-2013 REGRESSION: Fix in 1.02 does not repeat recorded 47 | " register when the mappings in repeat.vim and 48 | " visualrepeat.vim differ. We actually need to 49 | " always check g:repeat_sequence, since that is 50 | " also installed in g:repeat_reg[0]. Caught by 51 | " tests/ReplaceWithRegister/repeatLineAsVisual001.vim; 52 | " if only I had executed the tests sooner :-( 53 | " Fix by checking for the variable's existence 54 | " instead of using l:repeat_sequence. 55 | " 1.02.008 27-Dec-2012 BUG: "E121: Undefined variable: 56 | " g:repeat_sequence" when using visual repeat 57 | " of a mapping using registers without having used 58 | " repeat.vim beforehand. 59 | " 1.01.007 05-Apr-2012 FIX: Avoid error about undefined g:repeat_reg 60 | " when (a proper version of) repeat.vim isn't 61 | " available. 62 | " 1.00.006 12-Dec-2011 Catch any errors from the :normal . repetitions 63 | " instead of causing function errors. Also use 64 | " exceptions for the internal error signaling. 65 | " 005 06-Dec-2011 Retire visualrepeat#set_also(); it's the same as 66 | " visualrepeat#set() since we've dropped the 67 | " forced increment of b:changedtick. 68 | " 004 22-Oct-2011 BUG: Must initialize g:visualrepeat_tick on load 69 | " to avoid "Undefined variable" error in autocmds 70 | " on BufWrite. It can happen that this autoload 71 | " script is loaded without having a repetition 72 | " registered at the same time. 73 | " 003 21-Oct-2011 Also apply the same-register repeat enhancement 74 | " to repeat.vim here. 75 | " 002 17-Oct-2011 Increment b:changedtick without clobbering the 76 | " expression register. 77 | " Must also adapt g:visualrepeat_tick on buffer 78 | " save to allow repetition after a save and buffer 79 | " switch (without relying on g:repeat_sequence 80 | " being identical to g:visualrepeat_sequence, 81 | " which has formerly often saved us). 82 | " Omit own increment of b:changedtick, let the 83 | " mapping do that (or not, in case of a 84 | " non-modifying mapping). It seems to work without 85 | " it, and avoids setting the 'modified' flag on 86 | " unmodified buffers, which is not expected. 87 | " 001 17-Mar-2011 file creation 88 | let s:save_cpo = &cpo 89 | set cpo&vim 90 | 91 | let g:visualrepeat_tick = -1 92 | 93 | function! visualrepeat#set( sequence, ... ) 94 | let g:visualrepeat_sequence = a:sequence 95 | let g:visualrepeat_count = a:0 ? a:1 : v:count 96 | let g:visualrepeat_tick = b:changedtick 97 | endfunction 98 | 99 | 100 | let s:virtcol = 1 101 | function! visualrepeat#CaptureVirtCol() 102 | let s:virtcol = virtcol('.') 103 | return '' 104 | endfunction 105 | function! visualrepeat#repeatOnVirtCol( virtcol, count ) 106 | execute 'normal!' a:virtcol . '|' 107 | if virtcol('.') >= a:virtcol 108 | execute 'normal' a:count . '.' 109 | endif 110 | endfunction 111 | function! s:RepeatOnRange( range, command ) 112 | " The use of :global keeps track of lines added or deleted by the repeat, so 113 | " that we apply exactly to the selected lines. 114 | execute a:range . "global/^/" . a:command 115 | call histdel('search', -1) 116 | endfunction 117 | function! visualrepeat#repeat() 118 | if g:visualrepeat_tick == b:changedtick 119 | " visualrepeat.vim should handle the repeat. 120 | let l:repeat_sequence = g:visualrepeat_sequence 121 | let l:repeat_count = g:visualrepeat_count 122 | elseif exists('g:repeat_tick') && g:repeat_tick == b:changedtick 123 | " repeat.vim is enabled and would handle a normal-mode repeat. 124 | let l:repeat_sequence = g:repeat_sequence 125 | let l:repeat_count = g:repeat_count 126 | endif 127 | 128 | if exists('l:repeat_sequence') 129 | " A mapping for visualrepeat.vim or repeat.vim to repeat has been set. 130 | " Ensure that a corresponding visual mode mapping exists; some plugins 131 | " that only use repeat.vim may not have this. 132 | if ! empty(maparg(substitute(l:repeat_sequence, '^.\{3}', '', 'g'), 'x')) 133 | " Handle mappings that use a register and want the same register 134 | " used on repetition. 135 | let l:reg = '' 136 | if exists('g:repeat_reg') && exists('g:repeat_sequence') && 137 | \ g:repeat_reg[0] ==# g:repeat_sequence && ! empty(g:repeat_reg[1]) 138 | if g:repeat_reg[1] ==# '=' 139 | " This causes a re-evaluation of the expression on repeat, which 140 | " is what we want. 141 | let l:reg = '"=' . getreg('=', 1) . "\" 142 | else 143 | let l:reg = '"' . g:repeat_reg[1] 144 | endif 145 | endif 146 | 147 | " The normal mode mapping to be repeated has a corresponding visual 148 | " mode mapping. Use this so that the repetition will affect the 149 | " current selection. With this we also avoid the clumsy application 150 | " of the normal mode command to the visual selection, and can 151 | " support blockwise visual mode. 152 | let l:cnt = l:repeat_count == -1 ? '' : (v:count ? v:count : (l:repeat_count ? l:repeat_count : '')) 153 | 154 | call feedkeys('gv' . l:reg . l:cnt, 'n') 155 | call feedkeys(l:repeat_sequence) 156 | return 1 157 | endif 158 | endif 159 | 160 | try 161 | " Note: :normal has no bang to allow a remapped '.' command here to 162 | " enable repeat.vim functionality. 163 | 164 | if visualmode() ==# 'v' 165 | " Repeat the last change starting from the current cursor position. 166 | execute 'normal' (v:count ? v:count : '') . '.' 167 | elseif visualmode() ==# 'V' 168 | let [l:changeStart, l:changeEnd] = [getpos("'<"), getpos("'>")] 169 | 170 | " For all selected lines, repeat the last change in the line. 171 | if s:virtcol == 1 172 | " The cursor is set to the first column. 173 | call s:RepeatOnRange("'<,'>", 'normal ' . (v:count ? v:count : '') . '.') 174 | else 175 | " The cursor is set to the cursor column; the last change is 176 | " only applied to lines that have at least that many characters. 177 | call s:RepeatOnRange("'<,'>", printf('call visualrepeat#repeatOnVirtCol(%d, %s)', 178 | \ s:virtcol, 179 | \ string(v:count ? v:count : '') 180 | \)) 181 | endif 182 | 183 | call setpos("'[", l:changeStart) 184 | call setpos("']", l:changeEnd) 185 | else 186 | " Yank the selected block and repeat the last change in scratch 187 | " lines at the end of the buffer (using a different buffer would be 188 | " easier, but the repeated command may depend on the current 189 | " buffer's settings), so that the change is limited to the 190 | " selection. The vis.vim plugin does the same, but we cannot use it, 191 | " because it performs the movement (to the bottom of the current 192 | " buffer) via regular paste commands (which clobber the repeat 193 | " command). We need to be careful to avoid doing that, using only 194 | " lower level functions. 195 | let l:changeStart = getpos("'<") 196 | let l:startVirtCol = virtcol("'<") 197 | let [l:count, l:startColPattern, l:startLnum, l:endLnum, l:finalLnum] = [v:count, ('\%>' . (l:startVirtCol - 1) . 'v'), line("'<"), line("'>"), line('$')] 198 | let l:selection = split(ingo#selection#Get(), '\n', 1) 199 | 200 | " Save the view after the yank so that the cursor resides at the 201 | " beginning of the selected block, just as we would expect after the 202 | " repeat. (The :normal / :delete of the temporary range later 203 | " modifies the cursor position.) 204 | let l:save_view = winsaveview() 205 | 206 | let l:tempRange = (l:finalLnum + 1) . ',$' 207 | call append(l:finalLnum, l:selection) 208 | " The cursor is set to the first column. 209 | call s:RepeatOnRange(l:tempRange, 'normal ' . (l:count ? l:count : '') . '.') 210 | let l:result = getline(l:finalLnum + 1, '$') 211 | try 212 | " Using :undo to roll back the append and repeat is safer, 213 | " because any potential modification outside the temporary range 214 | " is also eliminated. Only explicitly delete the temporary range 215 | " as a fallback. 216 | silent undo 217 | catch /^Vim\%((\a\+)\)\=:E/ 218 | silent! execute l:tempRange . 'delete _' 219 | endtry 220 | 221 | for l:lnum in range(l:startLnum, l:endLnum) 222 | let l:idx = l:lnum - l:startLnum 223 | let l:line = getline(l:lnum) 224 | let l:startCol = match(l:line, l:startColPattern) 225 | let l:endCol = l:startCol + len(l:selection[l:idx]) 226 | let l:resultLine = get(l:result, l:idx, '') " Replace the line part with an empty string if there are less lines after the repeat. 227 | let l:newLine = strpart(l:line, 0, l:startCol) . l:resultLine . strpart(l:line, l:endCol) 228 | call setline(l:lnum, l:newLine) 229 | endfor 230 | 231 | let l:addedNum = len(l:result) - l:idx - 1 232 | if l:addedNum == 0 233 | let l:changeEnd = [0, l:lnum, l:startCol + len(l:resultLine), 0] 234 | else 235 | " The repeat has introduced additional lines; append those (as 236 | " new lines) properly indented to the start of the blockwise 237 | " selection. 238 | let l:indent = repeat(' ', l:startVirtCol - 1) 239 | 240 | " To use the buffer's indent settings, first insert spaces and 241 | " have :retab convert those to the proper indent. Then, append 242 | " the additional lines. 243 | call append(l:lnum, repeat([l:indent], l:addedNum)) 244 | 245 | silent execute printf('%d,%dretab!', l:lnum + 1, l:lnum + l:addedNum + 1) 246 | 247 | for l:addedIdx in range(l:addedNum) 248 | let l:addedLnum = l:lnum + 1 + l:addedIdx 249 | call setline(l:addedLnum, getline(l:addedLnum) . l:result[l:idx + 1 + l:addedIdx]) 250 | endfor 251 | 252 | let l:changeEnd = [0, l:addedLnum, len(getline(l:addedLnum)) + 1, 0] 253 | endif 254 | 255 | " The change marks still point to the (removed) temporary range. 256 | " Make them valid by setting them to the changed selection. 257 | call setpos("'[", l:changeStart) 258 | call setpos("']", l:changeEnd) 259 | 260 | call winrestview(l:save_view) 261 | endif 262 | return 1 263 | catch /^Vim\%((\a\+)\)\=:E117:.*ingo#selection#Get/ " E117: Unknown function: ingo#selection#Get 264 | let s:errorMsg = 'For blockwise repeat, you need to install the ingo-library dependency' 265 | catch /^Vim\%((\a\+)\)\=:/ 266 | " v:exception contains what is normally in v:errmsg, but with extra 267 | " exception source info prepended, which we cut away. 268 | let s:errorMsg = substitute(v:exception, '^\CVim\%((\a\+)\)\=:', '', '') 269 | catch 270 | let s:errorMsg = v:exception 271 | endtry 272 | 273 | return 0 274 | endfunction 275 | 276 | let s:errorMsg = '' 277 | function! visualrepeat#ErrorMsg() 278 | return s:errorMsg 279 | endfunction 280 | 281 | augroup visualrepeatPlugin 282 | autocmd! 283 | autocmd BufLeave,BufWritePre,BufReadPre * let g:visualrepeat_tick = (g:visualrepeat_tick == b:changedtick || g:visualrepeat_tick == 0) ? 0 : -1 284 | autocmd BufEnter,BufWritePost * if g:visualrepeat_tick == 0|let g:visualrepeat_tick = b:changedtick|endif 285 | augroup END 286 | 287 | let &cpo = s:save_cpo 288 | unlet s:save_cpo 289 | " vim: set ts=8 sts=4 sw=4 noexpandtab ff=unix fdm=syntax : 290 | -------------------------------------------------------------------------------- /autoload/visualrepeat/reapply.vim: -------------------------------------------------------------------------------- 1 | " visualrepeat/reapply.vim: Functions to cross-apply a visual mode mapping in a normal mode repeat. 2 | " 3 | " DEPENDENCIES: 4 | " 5 | " Copyright: (C) 2013 Ingo Karkat 6 | " The VIM LICENSE applies to this script; see ':help copyright'. 7 | " 8 | " Maintainer: Ingo Karkat 9 | " 10 | " REVISION DATE REMARKS 11 | " 1.10.001 18-Apr-2013 file creation 12 | 13 | function! visualrepeat#reapply#VisualMode( isStayInVisualMode ) 14 | "****D echomsg '****' v:count g:repeat_count 15 | let l:appendix = (a:isStayInVisualMode ? '' : "\") 16 | let l:isOnlyRepeatCount = (g:repeat_count == v:count) 17 | let l:count = (l:isOnlyRepeatCount ? 1 : v:count1) 18 | if ! l:isOnlyRepeatCount && visualmode() ==# 'V' 19 | " Select [count] lines, not [count] times the previously selected lines. 20 | " It would be more correct to do this for the other selection modes, 21 | " too, but it's difficult to multiply their size. 22 | return 'V' . (l:count > 1 ? l:count . '_' : '') . l:appendix 23 | else 24 | " A normal-mode repeat of the visual mapping is triggered by repeat.vim. 25 | " It establishes a new selection at the cursor position, of the same 26 | " mode and size as the last selection. 27 | " If [count] is given, the size is multiplied accordingly. This has 28 | " the side effect that a repeat with [count] will persist the expanded 29 | " size, which is different from what the normal-mode repeat does (it 30 | " keeps the scope of the original command). 31 | return l:count . 'v' . (&selection ==# 'exclusive' ? ' ' : '') . l:appendix 32 | " For ':set selection=exclusive', the final character must be 33 | " re-included with , but only if this is not linewise visual 34 | " mode; in that case, the would add the next line in case the 35 | " last selected line is empty. 36 | endif 37 | endfunction 38 | function! visualrepeat#reapply#RepeatCount() 39 | return (g:repeat_count ? g:repeat_count : '') 40 | endfunction 41 | " Note: We don't need to check for the existence of g:repeat_count here; the 42 | " normal mode repeat mapping of a visual mode mapping can only be triggered 43 | " through an installed repeat.vim. 44 | 45 | 46 | finish 47 | " Note: The count cannot be injected inside the :normal; it is ignored: 48 | nmap :execute 'normal! gv32':echomsg '****' v:count 49 | " What works is prepending via a map-expr: 50 | nmap :execute 'normal! gv'32:echomsg '****' v:count 51 | " But somehow only when used with : Ex commands, not normal-mode commands; this 52 | " fails: 53 | nmap :execute 'normal! gv'32A$ 54 | " But this works again: 55 | nmap :execute 'normal! gv'32:execute 'normal! gv' . v:count . "A$\Esc>" 56 | 57 | " This is how you would use this in your plugin; we don't define a 58 | " -mapping here, as you probably want to keep using :noremap (now with 59 | "