├── .gitignore ├── README.md ├── autoload ├── EasyClip.vim └── EasyClip │ ├── BlackHole.vim │ ├── Move.vim │ ├── Paste.vim │ ├── Shared.vim │ ├── Substitute.vim │ └── Yank.vim ├── doc └── easyclip.txt ├── plugin └── EasyClip.vim ├── spec ├── easyclip │ └── easyclip_spec.rb ├── fixtures │ └── repeat.vim └── spec_helper.rb └── tests ├── Test1.py ├── TestVimRc.vim └── vimfiles └── plugin └── repeat.vim /.gitignore: -------------------------------------------------------------------------------- 1 | doc/tags 2 | /project_root_marker 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | vim-easyclip 2 | ============= 3 | 4 | ### NOTE 5 | 6 | This plugin is considered more or less "done". Minor bug fixes will be applied but otherwise it will just remain stable. For modern versions of vim (Vim 8+ or Neovim) it has been split up into three different plugins instead: [vim-cutlass](https://github.com/svermeulen/vim-cutlass), [vim-yoink](https://github.com/svermeulen/vim-yoink), and [vim-subversive](https://github.com/svermeulen/vim-subversive) 7 | 8 | ### Simplified clipboard functionality for Vim. 9 | 10 | Author: [Steve Vermeulen](https://github.com/svermeulen), based on work by [Max Brunsfeld](http://www.github.com/maxbrunsfeld) 11 | 12 | EasyClip is a plugin for Vim which contains a collection of clipboard related 13 | functionality with the goal of making using the clipboard in Vim simpler and more intuitive 14 | without losing any of its power. 15 | 16 | A good starting point for the motivation behind this Vim plugin can be found in Drew Neil's post [Registers: The Good, the Bad, and the Ugly Parts](http://vimcasts.org/blog/2013/11/registers-the-good-the-bad-and-the-ugly-parts/) 17 | 18 | ## Table Of Contents 19 | 20 | * Installation 21 | * Black Hole Redirection 22 | * Substitution Operator 23 | * Yank Buffer 24 | * Paste 25 | * Persistent yank history and sharing clipboard between concurrent Vim instances 26 | * Clipboard setting 27 | * Options 28 | * Default Key Mappings 29 | * Custom Yanks 30 | * Feedback 31 | * Changelog 32 | * License 33 | 34 | ### Installation ### 35 | 36 | I recommend loading your plugins with [neobundle](https://github.com/Shougo/neobundle.vim) or [vundle](https://github.com/gmarik/vundle) or [pathogen](https://github.com/tpope/vim-pathogen) 37 | 38 | This plugin also requires that you have Tim Pope's [repeat.vim](https://github.com/tpope/vim-repeat) plugin installed 39 | 40 | ### Black Hole Redirection ### 41 | 42 | By default, Vim's built-in delete operator will yank the deleted text in addition to just deleting it. This works great when you want to cut text and paste it somewhere else, but in many other cases it can make things more difficult. For example, if you want to make some tiny edit to fix formatting after cutting some text, you either have to have had the foresight to use a named register, or specify the black hole register explicitly to do your formatting. This plugin solves that problem by redirecting all change and delete operations to the black hole register and introducing a new operator, 'cut' (by default this is mapped to the `m` key for 'move'). 43 | 44 | There is simply no need to clutter up the yank history with every single edit, when you almost always know at the time you are deleting text whether it's something that is worth keeping around or not. 45 | 46 | **NOTE** As a result of the above, by default easyclip will shadow an important vim function: The Add Mark key (`m`). Therefore either you will want to use a different key for the 'cut' operator (see options section below for this) or remap something else to 'add mark'. For example, to use `gm` for 'add mark' instead of `m`, include the following in your vimrc: 47 | 48 | nnoremap gm m 49 | 50 | ### Substitution Operator ### 51 | 52 | Because replacing text is such a common operation, EasyClip includes a motion for it. It is essentially equivalent to doing a change operation then pasting using the specified register. For example, assuming you have mapped this motion to the `s` key, to paste over the word under the cursor you would type `siw`, or to paste inside brackets, `si(`, etc. 53 | 54 | It can also take a register to use for the substitution (eg. `"asip`), and is fully repeatable using the `.` key. 55 | 56 | **NOTE** This feature is off by default. To use, you have to either enable the option `g:EasyClipUseSubstituteDefaults` (in which case it will be mapped to the `s` key) or map the key/keys of your choice to the `` mappings found in substitute.vim. 57 | 58 | ### Yank Buffer ### 59 | 60 | EasyClip allows you to yank and cut things without worrying about losing text that you copied previously. It achieves this by storing all yanks into a buffer, which you can cycle through forward or backwards to choose the yank that you want 61 | 62 | This works very similar to the way [YankRing](https://github.com/vim-scripts/YankRing.vim) and [YankStack](https://github.com/maxbrunsfeld/vim-yankstack) work, in that you can use a key binding to toggle between different yanks immediately after triggering a paste or substitute. (Most of the functionality is actually taken and adapted from Yankstack, with changes to make it work with substitute) 63 | 64 | By default, the keys to toggle the paste are mapped to `` and `` (similar to yankring). For example, executing `p` will paste, then toggle it to the most recent yank before that. You can continue toggling forwards/backwards in the yank history to replace the most recent paste as much as you want. Note that the toggle action will of course not be included in the undo history. That is, pressing undo after any number of swaps will undo the paste and not each swap. 65 | 66 | This method of toggling the chosen yank after paste will probably be your primary method of digging back into the yank buffer. Note that in this case the yank buffer is unchanged. What this means for example is that you can toggle a given paste back using `` 10 times, then if you perform a new paste in a different location it will still use the most recent yank (and not the final yank you arrived at after 10 swaps). 67 | 68 | Alternatively, you can execute keys `[y` or `]y` to navigate the yank buffer 'head' forwards or backwards. In this case the change will be permanent. That is, pressing `[y[yp` will paste the third most recent yank. Subsequent pastes will use the same yank, until you go forwards again using `]y`. 69 | 70 | NOTE: The [y and ]y mappings are not on by default (map them manually). 71 | 72 | You can view the full list of yanks at any time by running the command `:Yanks` 73 | 74 | Note that you can swap substitution operations in the same way as paste. 75 | 76 | Every time the yank buffer changes, it also populates all the numbered registers. `"1` is therefore the previous yank, `"2` is the yank before that, etc. This is similar to how the numbered registers work by default (but a bit more sane). (Credit to Drew Neil for the suggestion) 77 | 78 | Also, see `g:EasyClipPreserveCursorPositionAfterYank` option below for an optional non standard customization to yank 79 | 80 | ### Paste ### 81 | 82 | By default EasyClip preserves the default vim paste behaviour, which is the following: 83 | 84 | * `p` (lowercase) pastes text after the current line if the pasted text is multiline (or after the current character if non-multiline) 85 | * `P` (uppercase) behaves the same except acts before the current line (or before the current character if non-multiline) 86 | 87 | When the text is multi-line, the cursor is placed at the start of the new text. When the paste is non-multiline, the cursor is placed at the end. 88 | 89 | Alternatively, you can enable the option `g:EasyClipAlwaysMoveCursorToEndOfPaste` to have the cursor positioned at the end in both cases (off by default). Note that when this option is enabled, the beginning of the multi-line text is added to the jumplist, so you can still return to the start of the paste by pressing `` (and this applies to multi-line substitutions as well) 90 | 91 | Another non-standard option is `g:EasyClipAutoFormat` (off by default), which will automatically format text immediately after it is pasted. This can be useful when pasting text from one indent level to another. 92 | 93 | When auto-format is enabled, you can also map a key to toggle between the formatted paste and unformatted paste. For example, you might include something like the following in your .vimrc: 94 | 95 | nmap cf EasyClipToggleFormattedPaste 96 | 97 | Then anytime you want to view the original formatting you can type `cf` directly after paste. You can also continuing hitting `cf` again to toggle between format/unformatted. I find that in most cases I want to always auto-format, and for every other case I can cancel the auto-format immediately afterwards using this plug mapping. 98 | 99 | Easy Clip also includes a mapping for insert mode paste, which automatically turns on 'paste' mode for the duration of the paste. Using 'paste' mode will work much more intuitively when pasting text with multiple lines while in insert mode. You can enable this by including something similar to the following in your .vimrc: 100 | 101 | imap EasyClipInsertModePaste 102 | 103 | Note: If you have a custom mapping for `pastetoggle`, this may cause conflicts. To preserve the functionality of your existing custom map, you may want to enable the option `g:EasyClipUseGlobalPasteToggle`. See the comment at the top of `Paste.vim` for a more detailed explanation. 104 | 105 | For convenience, there is also a plug for command mode paste, which you can enable with the following 106 | 107 | cmap EasyClipCommandModePaste 108 | 109 | There is also the `:IPaste` command (aka interactive paste) which allows you to enter the yank index buffer you want to paste from. This can be useful if you are looking for an old yank and don't want to cycle back many times to find it. In these cases, execute `:IPaste`, then the entire yank buffer will be printed, then enter the index of the row in the printed table you want to paste from. 110 | 111 | ### Persistent yank history and sharing clipboard between concurrent Vim instances ### 112 | 113 | EasyClip can automatically store the yank history to file, so that it can be restored the next time you start Vim. Storing it to file also allows other active Vim instances to seamlessly share the same clipboard and yank history. 114 | 115 | You can enable this feature by enabling the option `g:EasyClipShareYanks` (NOTE: off by default). You can also customize where the yank history file gets stored (see options section below) 116 | 117 | Note that this feature can be [slow](https://github.com/svermeulen/vim-easyclip/issues/87) in some cases and this is why it is off by default. 118 | 119 | ### Clipboard setting ### 120 | 121 | Vim's built-in setting for `clipboard` can be set to one of the following: 122 | 123 | 1. set clipboard= 124 | 1. set clipboard=unnamed 125 | 1. set clipboard=unnamedplus 126 | 1. set clipboard=unnamed,unnamedplus 127 | 128 | Leaving it as (1) which is Vim's default, will cause all yank/delete/paste operations to use the `"` register. The only drawback here is that whenever you want to copy/paste something from another application, you have to explicitly access the system clipboard, which is represented by the `*` register. For example, to copy the current line to the system clipboard, you would type `"*yy`. And to paste some text copied from another window, you would type `"*p` 129 | 130 | To avoid this extra work, you can use option (2) and set it to `unnamed`. This will cause all yank/delete/paste operations to use the system register `*`. This way, you can copy something in Vim then immediately paste it into another application. And vice versa when returning to vim. 131 | 132 | I recommend using one of these two options. I personally use option (2). 133 | 134 | When option (3) is enabled, both Vim and EasyClip will use the `+` register as its default. 135 | 136 | Option (4) is the same as option (3), except Vim will also automatically copy the contents of the `+` register to the `*` register. 137 | 138 | ### Options ### 139 | 140 | EasyClip can be easily customized to whatever mappings you wish, using the following options: 141 | 142 | `g:EasyClipAutoFormat` - Default: 0. Set this to 1 to enable auto-formatting pasting text 143 | 144 | `g:EasyClipYankHistorySize` - Default: 50. Change this to limit yank history 145 | 146 | `g:EasyClipCopyExplicitRegisterToDefault` - Default: 0. When set to 0, easy-clip will not change the default register clipboard when an explicit register is given. For example, when set to 0, if you type `"ayip` it will copy the current paragraph to the `a` register, but it will not affect the default register, so typing `p` will work the same as it did before the above command. When set to 1, typing `"ayip` will copy the paragraph to both. 147 | 148 | `g:EasyClipAlwaysMoveCursorToEndOfPaste` - Default: 0. Set this to 1 to always position cursor at the end of the pasted text for both multi-line and non-multiline pastes. 149 | 150 | `g:EasyClipPreserveCursorPositionAfterYank` - Default 0 (ie. disabled). Vim's default behaviour is to position the cursor at the beginning of the yanked text, which is consistent with other motions. However if you prefer the cursor position to remain unchanged when performing yanks, enable this option. 151 | 152 | `g:EasyClipShareYanks` - Default: 0 (ie. disabled). When enabled, yank history is saved to file, which allows other concurrent Vim instances to automatically share the yank history, and also allows yank history to be automatically restored when restarting vim. 153 | 154 | `g:EasyClipShareYanksFile` - Default: '.easyclip'. The name of the file to save the yank history to when `g:EasyClipShareYanks` is enabled. 155 | 156 | `g:EasyClipShareYanksDirectory` - Default: '$HOME'. The directory to use to store the file with name given by `g:EasyClipShareYanksFile` setting. Only applicable when `g:EasyClipShareYanks` option is enabled. 157 | 158 | `g:EasyClipShowYanksWidth` - Default: 80 - The width to display for each line when the `Yanks` command is executed 159 | 160 | You can also disable the default mappings by setting one or more of the following to zero. By default they are set to 1 (ie. enabled) 161 | 162 | `g:EasyClipUseYankDefaults` 163 | 164 | `g:EasyClipUseCutDefaults` 165 | 166 | `g:EasyClipUsePasteDefaults` 167 | 168 | `g:EasyClipEnableBlackHoleRedirect` 169 | 170 | `g:EasyClipUsePasteToggleDefaults` 171 | 172 | One exception to the above is substitute, which is 0 by default (ie. disabled) 173 | 174 | `g:EasyClipUseSubstituteDefaults` 175 | 176 | To change from the default mappings, you can disable one of the options above and then map to the specific `` mappings of your choice. For example, to change the mapping for cut (by default set to `m`) to `x`, include the following in your vimrc:` 177 | 178 | let g:EasyClipUseCutDefaults = 0 179 | 180 | nmap x MoveMotionPlug 181 | xmap x MoveMotionXPlug 182 | nmap xx MoveMotionLinePlug 183 | 184 | Or to change the bindings for toggling paste from `` and `` to `` and `` include the following: 185 | 186 | let g:EasyClipUsePasteToggleDefaults = 0 187 | 188 | nmap EasyClipSwapPasteForward 189 | nmap EasyClipSwapPasteBackwards 190 | 191 | Or to use `gs` for substitute include the following: (in this case you don't need to turn off the default since the default is already disabled) 192 | 193 | nmap gs SubstituteOverMotionMap 194 | nmap gss SubstituteLine 195 | xmap gs XEasyClipPaste 196 | 197 | For reference, or other kinds of mappings, see the Plugs section of the file with the name of the operation you wish to remap (vim-easy-clip/autoload/substitute.vim / move.vim / yank.vim /etc.) 198 | 199 | Note that EasyClip will only enable a default mapping if it hasn't already been mapped to something in your .vimrc. 200 | 201 | ### Default Key Mappings ### 202 | 203 | `d` - Delete over the given motion and *do not* change clipboard 204 | 205 | `dd` - Delete the line and *do not* change clipboard 206 | 207 | `D` - Delete from cursor to the end of the line and *do not* change clipboard 208 | 209 | `dD` - Delete the contents of line except the newline character (that is, make it blank) and *do not* change clipboard 210 | 211 | `x` - Delete the character under cursor and *do not* change clipboard 212 | 213 | `s` - Delete the character under cursor then enter insert mode and *do not* change clipboard 214 | 215 | `S` - Delete the line under cursor then enter insert mode and *do not* change clipboard 216 | 217 | `c` - Enter insert mode over top the given area and *do not* change clipboard 218 | 219 | `cc` - Enter insert mode over top the current line and *do not* change clipboard 220 | 221 | `C` - Enter insert mode from cursor to the end of the line and *do not* change clipboard 222 | 223 | `p` - Paste from specified register. Inserts after current line if text is multiline, after current character if text is non-multiline. Leaves cursor at end of pasted text. 224 | 225 | `P` - Same as p except inserts text before current line/character 226 | 227 | `p` - Same as `p` except does not auto-format text. This is only relevant if the auto-format option is enabled 228 | 229 | `P` - Same as `P` except does not auto-format text. This is only relevant if the auto-format option is enabled 230 | 231 | `gp` - Same as p but preserves the current cursor position 232 | 233 | `gP` - Same as P but preserves the current cursor position 234 | 235 | `gP` - Same as `P` but preserves the current cursor position 236 | 237 | `gp` - Same as `p` but preserves the current cursor position 238 | 239 | `m` - Delete over the given motion and copy text to clipboard 240 | 241 | `mm` - Delete the current line and copy text to clipboard 242 | 243 | *NOTE*: `M` is NOT mapped by default. If you want it, include the following in your .vimrc: 244 | 245 | `nmap M MoveMotionEndOfLinePlug` 246 | 247 | `` - Rotate the previous paste forward in yank buffer. Note that this binding will only work if executed immediately after a paste 248 | 249 | `` - Rotate the previous paste backward in yank buffer. Note that this binding will only work if executed immediately after a paste 250 | 251 | `[y` - Go backward in the yank buffer. This can be executed at any time to modify order of yanks in the yank buffer (though I would recommend just using `` instead) 252 | 253 | `]y` - Go forward in the yank buffer. This can be executed at any time to modify order of yanks in the yank buffer (though I would recommend just using `` instead) 254 | 255 | `Y` - Copy text from cursor position to the end of line to the clipboard 256 | 257 | **When the option `g:EasyClipUseSubstituteDefaults` is enabled, the following mappings are added:** 258 | 259 | `s` - Substitute over the given motion with specified register (or default register if unspecified). Note that this only applies if the `g:EasyClipUseSubstituteDefaults` option is set. 260 | 261 | `ss` - Substitute over the current line with specified register (or default register if unspecified). Note that this only applies if the `g:EasyClipUseSubstituteDefaults` option is set. 262 | 263 | `gs` - Same as s but preserves the current cursor position. 264 | 265 | ### Custom Yanks ### 266 | 267 | If you have custom yanks that occur in your vimrc or elsewhere and would like them to be included in the yank history, you should call the EasyClip#Yank(). For example, to add a binding to yank the current file name you could add the following to your .vimrc: 268 | 269 | `nnoremap yf :call EasyClip#Yank(expand('%'))` 270 | 271 | Another way to do the above (which is necessary if you don't control the yank yourself), is to do the following: 272 | 273 | `nnoremap yf :EasyClipBeforeYank:let @*=expand('%'):EasyClipOnYanksChanged` 274 | 275 | Also, worth noting is the `Paste` command which takes an index and pastes the yank at that index. For example, executing `:Paste 0` is equivalent to `p`, `:Paste 1` is equivalent to `"1p`, etc. For use within scripting, there is also the corresponding method `EasyClip#PasteIndex` which like the command takes an index as parameter. For the `P` equivalent, there is also a `PasteBefore` command and `EasyClip#PasteIndexBefore` method. 276 | 277 | Another method worth noting is `EasyClip#GetYankAtIndex` which returns the text for the yank at a given index. 278 | 279 | ### Feedback ### 280 | 281 | Feel free to email all feedback/criticism/suggestions to sfvermeulen@gmail.com. Or, feel free to create a github issue. 282 | 283 | ### Changelog ### 284 | 285 | 2.4 (2015-11-19) 286 | - Added interactive paste by executing command `:PasteI` 287 | - Added `:Paste` and `:PasteBefore` commands, and also the corresponding methods `EasyClip#PasteIndex` and `EasyClip#PasteIndexBefore` 288 | 289 | 2.3 (2015-03-14) 290 | - Bug fixes to visual mode paste 291 | - Added plug mapping to toggle paste between format/unformatted 292 | 293 | 2.2 (2015-01-27) 294 | - Bug fixes 295 | - Removed the 'system sync' option since using unnamed register is sufficient for this 296 | - Added support for persistent/shared yanks 297 | 298 | 2.1 (2013-12-06) 299 | - Bug fixes 300 | - Disabled substitution operator by default 301 | - Added numbered registers 302 | 303 | 2.0 (2013-09-22) 304 | - Many bug fixes 305 | - Yankring/Yankstack style post-paste swap 306 | - RSPEC unit tests added for stability 307 | 308 | 1.2 (2013-09-22) 309 | - More bug fixes 310 | 311 | 1.1 (2013-09-03) 312 | - Bunch of bug fixes 313 | 314 | 1.0 (2013-07-08) 315 | - Initial release 316 | 317 | ### License ### 318 | 319 | Distributed under the same terms as Vim itself. See the vim license. 320 | 321 | -------------------------------------------------------------------------------- /autoload/EasyClip.vim: -------------------------------------------------------------------------------- 1 | 2 | """"""""""""""""""""""" 3 | " Global Options 4 | """"""""""""""""""""""" 5 | let g:EasyClipYankHistorySize = get(g:, 'EasyClipYankHistorySize', 50) 6 | let g:EasyClipShowYanksWidth = get(g:, 'EasyClipShowYanksWidth', 80) 7 | let g:EasyClipAutoFormat = get(g:, 'EasyClipAutoFormat', 0) 8 | let g:EasyClipEnableBlackHoleRedirect = get(g:, 'EasyClipEnableBlackHoleRedirect', 1) 9 | let g:EasyClipUseCutDefaults = get(g:, 'EasyClipUseCutDefaults', 1) 10 | let g:EasyClipUseSubstituteDefaults = get(g:, 'EasyClipUseSubstituteDefaults', 0) 11 | let g:EasyClipUsePasteToggleDefaults = get(g:, 'EasyClipUsePasteToggleDefaults', 1) 12 | let g:EasyClipUsePasteDefaults = get(g:, 'EasyClipUsePasteDefaults', 1) 13 | let g:EasyClipAlwaysMoveCursorToEndOfPaste = get(g:, 'EasyClipAlwaysMoveCursorToEndOfPaste', 0) 14 | let g:EasyClipUseYankDefaults = get(g:, 'EasyClipUseYankDefaults', 1) 15 | let g:EasyClipPreserveCursorPositionAfterYank = get(g:, 'EasyClipPreserveCursorPositionAfterYank', 0) 16 | let g:EasyClipShareYanks = get(g:, 'EasyClipShareYanks', 0) 17 | let g:EasyClipShareYanksFile = get(g:, 'EasyClipShareYanksFile', '.easyclip') 18 | let g:EasyClipShareYanksDirectory = get(g:, 'EasyClipShareYanksDirectory', '$HOME') 19 | let g:EasyClipCopyExplicitRegisterToDefault = get(g:, 'EasyClipCopyExplicitRegisterToDefault', 0) 20 | 21 | let g:EasyClipEnableBlackHoleRedirectForChangeOperator = get(g:, 'EasyClipEnableBlackHoleRedirectForChangeOperator', 1) 22 | let g:EasyClipEnableBlackHoleRedirectForDeleteOperator = get(g:, 'EasyClipEnableBlackHoleRedirectForDeleteOperator', 1) 23 | let g:EasyClipEnableBlackHoleRedirectForSelectOperator = get(g:, 'EasyClipEnableBlackHoleRedirectForSelectOperator', 1) 24 | 25 | """"""""""""""""""""""" 26 | " Commands 27 | """"""""""""""""""""""" 28 | command! -nargs=0 IPaste call EasyClip#InteractivePaste(0) 29 | command! -nargs=0 IPasteBefore call EasyClip#InteractivePaste(1) 30 | command! -nargs=1 Paste call EasyClip#PasteIndex() 31 | command! -nargs=1 PasteBefore call EasyClip#PasteIndexBefore() 32 | command! EasyClipBeforeYank :call EasyClip#Yank#OnBeforeYank() 33 | command! EasyClipOnYanksChanged :call EasyClip#Yank#OnYanksChanged() 34 | command! -nargs=0 Yanks call EasyClip#Yank#ShowYanks() 35 | command! -nargs=0 ClearYanks call EasyClip#Yank#ClearYanks() 36 | 37 | """"""""""""""""""""""" 38 | " Functions 39 | """"""""""""""""""""""" 40 | function! EasyClip#GetDefaultReg() 41 | let clipboard_flags = split(&clipboard, ',') 42 | if index(clipboard_flags, 'unnamedplus') >= 0 43 | return "+" 44 | elseif index(clipboard_flags, 'unnamed') >= 0 45 | return "*" 46 | else 47 | return "\"" 48 | endif 49 | endfunction 50 | 51 | " Only add the given mapping if it doesn't already exist 52 | function! EasyClip#AddWeakMapping(left, right, modes, ...) 53 | let recursive = a:0 > 0 ? a:1 : 0 54 | 55 | for mode in split(a:modes, '\zs') 56 | if !EasyClip#HasMapping(a:left, mode) 57 | exec mode . (recursive ? "map" : "noremap") . " " . a:left . " " . a:right 58 | endif 59 | endfor 60 | endfunction 61 | 62 | function! EasyClip#HasMapping(mapping, mode) 63 | return maparg(a:mapping, a:mode) != '' 64 | endfunction 65 | 66 | function! EasyClip#GetCurrentYank() 67 | return getreg(EasyClip#GetDefaultReg()) 68 | endfunction 69 | 70 | function! EasyClip#SetCurrentYank(yank) 71 | call setreg(EasyClip#GetDefaultReg(), a:yank) 72 | EasyClipOnYanksChanged 73 | endfunction 74 | 75 | function! EasyClip#Yank(str) 76 | EasyClipBeforeYank 77 | exec "let @". EasyClip#GetDefaultReg() . "='". a:str . "'" 78 | EasyClipOnYanksChanged 79 | endfunction 80 | 81 | function! EasyClip#GetYankAtIndex(index) 82 | return EasyClip#Yank#GetYankInfoForIndex(a:index).text 83 | endfunction 84 | 85 | function! EasyClip#InteractivePaste(pasteBefore) 86 | echohl WarningMsg | echo "--- Interactive Paste ---" | echohl None 87 | let i = 0 88 | for yank in EasyClip#Yank#EasyClipGetAllYanks() 89 | call EasyClip#Yank#ShowYank(yank, i) 90 | let i += 1 91 | endfor 92 | 93 | let indexStr = input('Index: ') 94 | 95 | if indexStr =~ '\v^\s*$' 96 | return 97 | endif 98 | 99 | if indexStr !~ '\v^\s*\d+\s*' 100 | echo "\n" 101 | echoerr "Invalid yank index given" 102 | else 103 | let index = str2nr(indexStr) 104 | 105 | if index < 0 || index > EasyClip#Yank#GetNumYanks() 106 | echo "\n" 107 | echoerr "Yank index out of bounds" 108 | else 109 | if a:pasteBefore 110 | call EasyClip#PasteIndexBefore(index) 111 | else 112 | call EasyClip#PasteIndex(index) 113 | endif 114 | endif 115 | endif 116 | endfunction 117 | 118 | function! EasyClip#PasteIndex(index) 119 | if a:index == 0 120 | exec "normal \EasyClipPasteAfter" 121 | else 122 | let oldYankHead = EasyClip#Yank#GetYankstackHead() 123 | call EasyClip#Yank#SetYankStackHead(EasyClip#Yank#GetYankInfoForIndex(a:index)) 124 | exec "normal \EasyClipPasteAfter" 125 | call EasyClip#Yank#SetYankStackHead(oldYankHead) 126 | endif 127 | endfunction 128 | 129 | function! EasyClip#PasteIndexBefore(index) 130 | if a:index == 0 131 | exec "normal \EasyClipPasteBefore" 132 | else 133 | let oldYankHead = EasyClip#Yank#GetYankstackHead() 134 | call EasyClip#Yank#SetYankStackHead(EasyClip#Yank#GetYankInfoForIndex(a:index)) 135 | exec "normal \EasyClipPasteBefore" 136 | call EasyClip#Yank#SetYankStackHead(oldYankHead) 137 | endif 138 | endfunction 139 | 140 | function! EasyClip#Init() 141 | call EasyClip#Paste#Init() 142 | call EasyClip#Move#Init() 143 | call EasyClip#Substitute#Init() 144 | call EasyClip#Yank#Init() 145 | call EasyClip#Shared#Init() 146 | 147 | " Add black hole bindings last so that it only 148 | " adds bindings if they are not taken 149 | call EasyClip#BlackHole#Init() 150 | endfunction 151 | -------------------------------------------------------------------------------- /autoload/EasyClip/BlackHole.vim: -------------------------------------------------------------------------------- 1 | 2 | function! EasyClip#BlackHole#AddSelectBindings() 3 | 4 | let i = 33 5 | 6 | " Add a map for every printable character to copy to black hole register 7 | " I see no easier way to do this 8 | while i <= 126 9 | if i !=# 124 10 | let char = nr2char(i) 11 | if i ==# 92 12 | let char = '\\' 13 | endif 14 | exec 'snoremap '. char .' "_c'. char 15 | endif 16 | 17 | let i = i + 1 18 | endwhile 19 | 20 | snoremap "_c 21 | snoremap "_c 22 | snoremap \| "_c| 23 | endfunction 24 | 25 | function! EasyClip#BlackHole#AddDeleteBindings() 26 | 27 | let bindings = 28 | \ [ 29 | \ ['d', '"_d', 'nx'], 30 | \ ['dd', '"_dd', 'n'], 31 | \ ['dD', '0"_d$', 'n'], 32 | \ ['D', '"_D', 'nx'], 33 | \ ['x', '"_x', 'nx'], 34 | \ ['X', '"_X', 'nx'], 35 | \ ] 36 | 37 | for binding in bindings 38 | call call("EasyClip#AddWeakMapping", binding) 39 | endfor 40 | endfunction 41 | 42 | function! EasyClip#BlackHole#AddChangeBindings() 43 | 44 | let bindings = 45 | \ [ 46 | \ ['c', '"_c', 'nx'], 47 | \ ['cc', '"_S', 'n'], 48 | \ ['C', '"_C', 'nx'], 49 | \ ['s', '"_s', 'nx'], 50 | \ ['S', '"_S', 'nx'], 51 | \ ] 52 | 53 | for binding in bindings 54 | call call("EasyClip#AddWeakMapping", binding) 55 | endfor 56 | endfunction 57 | 58 | function! EasyClip#BlackHole#Init() 59 | 60 | if g:EasyClipEnableBlackHoleRedirect 61 | 62 | if g:EasyClipEnableBlackHoleRedirectForChangeOperator 63 | call EasyClip#BlackHole#AddChangeBindings() 64 | endif 65 | 66 | if g:EasyClipEnableBlackHoleRedirectForDeleteOperator 67 | call EasyClip#BlackHole#AddDeleteBindings() 68 | endif 69 | 70 | if g:EasyClipEnableBlackHoleRedirectForSelectOperator 71 | call EasyClip#BlackHole#AddSelectBindings() 72 | endif 73 | endif 74 | endfunction 75 | -------------------------------------------------------------------------------- /autoload/EasyClip/Move.vim: -------------------------------------------------------------------------------- 1 | 2 | """"""""""""""""""""""" 3 | " Variables 4 | """"""""""""""""""""""" 5 | let s:activeRegister = EasyClip#GetDefaultReg() 6 | 7 | """"""""""""""""""""""" 8 | " Plugs 9 | """"""""""""""""""""""" 10 | nnoremap MoveMotionEndOfLinePlug :EasyClipBeforeYanky$:EasyClipOnYanksChanged"_d$:call repeat#set("\MoveMotionEndOfLinePlug") 11 | nnoremap MoveMotionReplaceLinePlug :EasyClipBeforeYank0y$:EasyClipOnYanksChanged"_d$:call repeat#set("\MoveMotionReplaceLinePlug") 12 | nnoremap MoveMotionLinePlug ':EasyClipBeforeYank'. v:count .'yy'. v:count . '"_dd:EasyClipOnYanksChanged:call repeat#set("\MoveMotionLinePlug")' 13 | xnoremap MoveMotionXPlug :call VisualModeMoveMotion(v:register) 14 | nnoremap MoveMotionPlug ":call EasyClip#Move#PreMoveMotion():set opfunc=EasyClip#Move#MoveMotion" . (v:count > 0 ? v:count : '') . "g@" 15 | 16 | """"""""""""""""""""""" 17 | " Functions 18 | """"""""""""""""""""""" 19 | function! s:VisualModeMoveMotion(reg) 20 | 21 | if a:reg == EasyClip#GetDefaultReg() || g:EasyClipCopyExplicitRegisterToDefault 22 | EasyClipBeforeYank 23 | normal! gvy 24 | normal! gv"_d 25 | EasyClipOnYanksChanged 26 | 27 | if g:EasyClipCopyExplicitRegisterToDefault 28 | call EasyClip#Yank#SetRegToYankInfo(a:reg, EasyClip#Yank#GetYankstackHead()) 29 | endif 30 | else 31 | let oldDefaultInfo = EasyClip#Yank#GetYankstackHead() 32 | " If register is specified explicitly then do not change default register 33 | " or add to yank history 34 | exec "normal! gv\"" . a:reg . "y" 35 | normal! gv"_d 36 | call EasyClip#Yank#SetYankStackHead(oldDefaultInfo) 37 | endif 38 | endfunction 39 | 40 | function! EasyClip#Move#PreMoveMotion( ) 41 | let s:activeRegister = v:register 42 | 43 | " This is necessary to get around a bug in vim where the active register persists to 44 | " the next command. Repro by doing "_d and then a command that uses v:register 45 | if s:activeRegister ==# "_" 46 | let s:activeRegister = EasyClip#GetDefaultReg( ) 47 | endif 48 | endfunction 49 | 50 | function! EasyClip#Move#MoveMotion(type) 51 | let oldSelection = &selection 52 | let &selection = 'inclusive' 53 | 54 | let oldVisualStart = getpos("'<") 55 | let oldVisualEnd = getpos("'>") 56 | let visualStart = getpos("'[") 57 | let visualEnd = getpos("']") 58 | 59 | let newType = a:type 60 | 61 | if newType ==# 'char' 62 | let numColumnsFirstLine = col([visualStart[1], '^']) 63 | let numColumnsLastLine = col([visualEnd[1], '$']) 64 | 65 | if visualStart[1] != visualEnd[1] && visualStart[2] == numColumnsFirstLine+1 && visualEnd[2] == numColumnsLastLine-1 66 | let newType = 'line' 67 | endif 68 | endif 69 | 70 | call EasyClip#Yank#_YankLastChangedText(newType, s:activeRegister) 71 | 72 | exe "keepjumps normal! `[" . (newType ==# 'line' ? 'V' : 'v') 73 | \ . "`]\"_d" 74 | 75 | call setpos("'<", oldVisualStart) 76 | call setpos("'>", oldVisualEnd) 77 | 78 | let &selection = oldSelection 79 | endfunction 80 | 81 | function! EasyClip#Move#SetDefaultBindings() 82 | 83 | let bindings = 84 | \ [ 85 | \ ['m', 'MoveMotionPlug', 'n', 1], 86 | \ ['mm', 'MoveMotionLinePlug', 'n', 1], 87 | \ ['m', 'MoveMotionXPlug', 'x', 1], 88 | \ ] 89 | 90 | " Leave these commented to avoid shadowing M (go to middle of screen) 91 | "\ ['M', 'MoveMotionEndOfLinePlug', 'n', 1], 92 | "\ ['mM', 'MoveMotionReplaceLinePlug', 'n', 1], 93 | 94 | for binding in bindings 95 | call call("EasyClip#AddWeakMapping", binding) 96 | endfor 97 | endfunction 98 | 99 | function! EasyClip#Move#Init() 100 | 101 | if g:EasyClipUseCutDefaults 102 | call EasyClip#Move#SetDefaultBindings() 103 | endif 104 | endfunction 105 | -------------------------------------------------------------------------------- /autoload/EasyClip/Paste.vim: -------------------------------------------------------------------------------- 1 | 2 | """"""""""""""""""""""" 3 | " Variables 4 | """"""""""""""""""""""" 5 | let s:pasteOverrideRegister = '' 6 | let s:lastPasteRegister ='' 7 | let s:lastPasteChangedtick = -1 8 | let s:offsetSum = 0 9 | let s:isSwapping = 0 10 | let s:lastPasteWasAutoFormatted = 0 11 | 12 | """"""""""""""""""""""" 13 | " Plugs 14 | """"""""""""""""""""""" 15 | 16 | " Always toggle to 'paste mode' before pasting in insert mode 17 | " We have two methods of doing this here, both with different advantages/disadvantages 18 | " The first modifies the global value for pastetoggle, which may be undesirable if you want to bind 19 | " pastetoggle to something yourself (also Neovim deprecated pastetoggle, so this doesn't apply) 20 | " The second avoids the need to set the global pastetoggle but leaves insert mode briefly, which can 21 | " cause the indentation level to change sometimes (for eg. when hitting 'o' then immediately doing CTRL+V to paste something) 22 | if get(g:, 'EasyClipUseGlobalPasteToggle', 1) && !has('nvim') 23 | set pastetoggle=PasteToggle 24 | imap EasyClipInsertModePaste 'PasteToggle' . EasyClip#GetDefaultReg() . 'PasteToggle' 25 | else 26 | inoremap EasyClipInsertModePaste ':setl paste' . EasyClip#GetDefaultReg() . ':setl nopaste' 27 | endif 28 | 29 | cnoremap EasyClipCommandModePaste '' . EasyClip#GetDefaultReg() 30 | 31 | nnoremap EasyClipSwapPasteForward :call EasyClip#Paste#SwapPaste(1) 32 | nnoremap EasyClipSwapPasteBackwards :call EasyClip#Paste#SwapPaste(0) 33 | 34 | nnoremap EasyClipPasteAfter :call EasyClip#Paste#PasteText(v:register, v:count, 'p', 1, "EasyClipPasteAfter") 35 | nnoremap EasyClipPasteBefore :call EasyClip#Paste#PasteText(v:register, v:count, 'P', 1, "EasyClipPasteBefore") 36 | 37 | xnoremap XEasyClipPaste ':call EasyClip#Paste#PasteTextVisualMode(''' . v:register . ''',' . v:count . ')' 38 | 39 | nnoremap G_EasyClipPasteAfter :call EasyClip#Paste#PasteText(v:register, v:count, 'gp', 1, "G_EasyClipPasteAfter") 40 | nnoremap G_EasyClipPasteBefore :call EasyClip#Paste#PasteText(v:register, v:count, 'gP', 1, "G_EasyClipPasteBefore") 41 | xnoremap XG_EasyClipPaste "_d:call EasyClip#Paste#PasteText(v:register, v:count, 'gP', 1, "G_EasyClipPasteBefore") 42 | 43 | nnoremap EasyClipPasteUnformattedAfter :call EasyClip#Paste#PasteText(v:register, v:count, 'p', 0, "EasyClipPasteUnformattedAfter") 44 | nnoremap EasyClipPasteUnformattedBefore :call EasyClip#Paste#PasteText(v:register, v:count, 'P', 0, "EasyClipPasteUnformattedBefore") 45 | 46 | nnoremap G_EasyClipPasteUnformattedAfter :call EasyClip#Paste#PasteText(v:register, v:count, 'gp', 0, "G_EasyClipPasteUnformattedAfter") 47 | nnoremap G_EasyClipPasteUnformattedBefore :call EasyClip#Paste#PasteText(v:register, v:count, 'gP', 0, "G_EasyClipPasteUnformattedBefore") 48 | 49 | nnoremap XEasyClipPasteUnformatted "_d:call EasyClip#Paste#PasteText(v:register, v:count, 'P', 0, "EasyClipPasteUnformattedBefore") 50 | 51 | xnoremap XG_EasyClipPasteUnformatted "_d:call EasyClip#Paste#PasteText(v:register, v:count, 'gP', 0, "G_EasyClipPasteUnformattedBefore") 52 | 53 | nnoremap EasyClipToggleFormattedPaste :call EasyClip#Paste#ToggleFormattedPaste() 54 | 55 | """"""""""""""""""""""" 56 | " Functions 57 | """"""""""""""""""""""" 58 | 59 | " Adds the following functionality to paste: 60 | " - add position of cursor before pasting to jumplist 61 | " - optionally auto format, preserving the marks `[ and `] in the process 62 | " - always position the cursor directly after the text for P and p versions 63 | " - do not move the cursor for gp and gP versions 64 | " 65 | " op = either P or p 66 | " format = 1 if we should autoformat 67 | " inline = 1 if we should paste multiline text inline. 68 | " That is, add the newline wherever the cursor is rather than above/below the current line 69 | function! EasyClip#Paste#Paste(op, format, reg, inline) 70 | if !&modifiable && &buftype != 'terminal' 71 | return 72 | endif 73 | 74 | " This shouldn't be necessary since we calling this on FocusGained but 75 | " we call it here anyway since some console vims don't fire FocusGained 76 | call EasyClip#Shared#LoadFileIfChanged() 77 | 78 | let reg = empty(s:pasteOverrideRegister) ? a:reg : s:pasteOverrideRegister 79 | 80 | let text = getreg(reg) 81 | let textType = getregtype(reg) 82 | 83 | if text ==# '' 84 | " Necessary to avoid error 85 | return 86 | endif 87 | 88 | let s:lastPasteRegister = reg 89 | let isMultiLine = (text =~# "\n") 90 | let line = getline('.') 91 | let isEmptyLine = (line =~# '^\s*$') 92 | let oldPos = getpos('.') 93 | 94 | if a:inline 95 | " Do not save to jumplist when pasting inline 96 | exec 'normal! ' . (a:op ==# 'p' ? 'a' : 'i') . "\" . reg . "\" 97 | else 98 | let hasMoreThanOneLine = (text =~# "\n.*\n") 99 | " Save their old position to jumplist 100 | " Except for gp since the cursor pos shouldn't change 101 | " in that case 102 | if hasMoreThanOneLine && g:EasyClipAlwaysMoveCursorToEndOfPaste 103 | if a:op ==# 'P' 104 | " just doing m` doesn't work in this case so do it one line above 105 | exec "normal! km`j" 106 | elseif a:op ==# 'p' 107 | exec "normal! m`" 108 | endif 109 | endif 110 | 111 | exec "normal! \"".reg.a:op 112 | endif 113 | 114 | let shouldAutoFormat = 0 115 | 116 | " Only auto-format if it's multiline or pasting into an empty line 117 | if (isMultiLine || isEmptyLine) 118 | if exists('s:ForceAutoFormat') 119 | let shouldAutoFormat = s:ForceAutoFormat 120 | else 121 | let shouldAutoFormat = a:format && g:EasyClipAutoFormat && get(b:, 'EasyClipAutoFormat', 1) 122 | endif 123 | endif 124 | 125 | if (shouldAutoFormat) 126 | let s:lastPasteWasAutoFormatted = 1 127 | keepjumps normal! `] 128 | let startPos = getpos('.') 129 | normal! ^ 130 | let numFromStart = startPos[2] - col('.') 131 | 132 | " Suppress 'x lines indented' message 133 | silent exec "keepjumps normal! `[=`]" 134 | call setpos('.', startPos) 135 | normal! ^ 136 | 137 | if numFromStart > 0 138 | " Preserve cursor position so that it is placed at the last pasted character 139 | exec 'normal! ' . numFromStart . 'l' 140 | endif 141 | 142 | normal! m] 143 | else 144 | let s:lastPasteWasAutoFormatted = 0 145 | endif 146 | 147 | if a:op ==# 'gp' 148 | call setpos('.', oldPos) 149 | 150 | " This is necessary to avoid the bug where going up or down 151 | " does not use the right column number 152 | if col('.') == col('$') - 1 153 | normal! hl 154 | else 155 | normal! lh 156 | endif 157 | 158 | elseif a:op ==# 'gP' 159 | exec "keepjumps normal! `[" 160 | 161 | else 162 | if isMultiLine 163 | if g:EasyClipAlwaysMoveCursorToEndOfPaste 164 | exec "keepjumps normal! `]" 165 | else 166 | exec "keepjumps normal! `[" 167 | endif 168 | 169 | " We do not want to always go to the beginning of the line when pasting 170 | " visual block mode text. Default behaviour is to retain the column position 171 | " Otherwise, we do want to go to the beginning of the line 172 | if len(textType) > 0 && textType[0] !=# '' 173 | normal! ^ 174 | endif 175 | else 176 | exec "keepjumps normal! `]" 177 | endif 178 | endif 179 | 180 | endfunction 181 | 182 | function! s:EndSwapPaste() 183 | 184 | if !s:isSwapping 185 | " Should never happen 186 | throw 'Unknown Error detected during EasyClip paste' 187 | endif 188 | 189 | let s:isSwapping = 0 190 | 191 | augroup SwapPasteMoveDetect 192 | autocmd! 193 | augroup END 194 | 195 | " Return yank positions to their original state before we started swapping 196 | call EasyClip#Yank#Rotate(-s:offsetSum) 197 | endfunction 198 | 199 | function! EasyClip#Paste#WasLastChangePaste() 200 | return b:changedtick == s:lastPasteChangedtick || b:changedtick == g:lastSubChangedtick 201 | endfunction 202 | 203 | function! EasyClip#Paste#PasteTextVisualMode(reg, count) 204 | normal! gv 205 | 206 | let vmode = mode() 207 | " If we're pasting a single line yank in visual block mode then repeat paste for each line 208 | if vmode ==# '' && getreg(a:reg) !~# '\n' 209 | call EasyClip#Shared#LoadFileIfChanged() 210 | exec "normal! \"_c\\" . EasyClip#GetDefaultReg() 211 | else 212 | let lnum = line('''>') 213 | let cnum = col('''>') 214 | let cols = col([lnum, '$']) 215 | 216 | " See here for an explanation of this code: 217 | " https://github.com/svermeulen/vim-easyclip/wiki/Details-of-Visual-mode-paste 218 | if vmode ==# 'v' 219 | let shouldPasteBefore = 220 | \ (cnum != cols - 1 && (lnum != line('$') || cnum != cols)) && 221 | \ (cnum != cols || col([lnum + 1, '$']) != 1) 222 | elseif vmode ==# 'V' 223 | let shouldPasteBefore = (lnum != line('$')) 224 | elseif vmode ==# '' 225 | let lnum = min([lnum, line('''<')]) 226 | let cnum = max([cnum, col('''<')]) 227 | let cols = col([lnum, '$']) 228 | 229 | let shouldPasteBefore = (cnum <= cols - 2 || cols <= 2) 230 | else 231 | " Should never happen 232 | throw 'Unknown error occurred during EasyClip paste' 233 | endif 234 | 235 | let [op, plugName] = shouldPasteBefore ? ['P', 'EasyClipPasteBefore'] : ['p', 'EasyClipPasteAfter'] 236 | let l:save_selection = [getpos('''<'), getpos('''>')] 237 | 238 | normal! "_d 239 | 240 | " Don't add blank line when pasting linewise to an empty buffer. 241 | let l:isEmptyBuffer = (vmode ==# 'V') && (line('$') == 1) && empty(getline(1)) 242 | if l:isEmptyBuffer | execute 'normal! gv' | endif 243 | 244 | call EasyClip#Paste#PasteText(a:reg, a:count, op, 1, plugName) 245 | 246 | call setpos('''<', l:save_selection[0]) 247 | call setpos('''>', l:save_selection[1]) 248 | endif 249 | endfunction 250 | 251 | function! EasyClip#Paste#PasteText(reg, count, op, format, plugName) 252 | 253 | let reg = a:reg 254 | 255 | " This is necessary to get around a bug in vim where the active register persists to 256 | " the next command. Repro by doing "_d and then a command that uses v:register 257 | if reg ==# '_' 258 | let reg = EasyClip#GetDefaultReg() 259 | end 260 | 261 | let i = 0 262 | let cnt = a:count > 0 ? a:count : 1 263 | 264 | while i < cnt 265 | call EasyClip#Paste#Paste(a:op, a:format, reg, 0) 266 | let i += 1 267 | endwhile 268 | 269 | let s:lastPasteChangedtick = b:changedtick 270 | 271 | if !empty(a:plugName) 272 | let fullPlugName = "\" . a:plugName 273 | silent! call repeat#setreg(fullPlugName, reg) 274 | silent! call repeat#set(fullPlugName, a:count) 275 | endif 276 | endfunction 277 | 278 | function! EasyClip#Paste#ToggleFormattedPaste() 279 | 280 | if !EasyClip#Paste#WasLastChangePaste() 281 | echo 'Last action was not paste, toggle unformat command ignored' 282 | return 283 | endif 284 | 285 | let s:ForceAutoFormat = !s:lastPasteWasAutoFormatted 286 | let s:pasteOverrideRegister = s:lastPasteRegister 287 | exec "normal \(RepeatUndo)\(RepeatDot)" 288 | let s:pasteOverrideRegister = '' 289 | echo (s:ForceAutoFormat ? 'Formatted' : 'Unformatted') 290 | unlet s:ForceAutoFormat 291 | 292 | endfunction 293 | 294 | function! EasyClip#Paste#SwapPaste(forward) 295 | if !EasyClip#Paste#WasLastChangePaste() 296 | if (a:forward) && exists('g:EasyClipSwapPasteForwardFallback') 297 | exec g:EasyClipSwapPasteForwardFallback 298 | elseif (!a:forward) && exists('g:EasyClipSwapPasteBackwardsFallback') 299 | exec g:EasyClipSwapPasteBackwardsFallback 300 | else 301 | echo 'Last action was not paste, swap ignored' 302 | endif 303 | return 304 | endif 305 | 306 | if s:isSwapping 307 | " Stop checking to end the swap session 308 | augroup SwapPasteMoveDetect 309 | autocmd! 310 | augroup END 311 | else 312 | let s:isSwapping = 1 313 | let s:offsetSum = 0 314 | endif 315 | 316 | if s:lastPasteRegister == EasyClip#GetDefaultReg() 317 | let offset = (a:forward ? 1 : -1) 318 | 319 | call EasyClip#Yank#Rotate(offset) 320 | let s:offsetSum += offset 321 | endif 322 | 323 | let s:pasteOverrideRegister = EasyClip#GetDefaultReg() 324 | exec 'normal u.' 325 | let s:pasteOverrideRegister = '' 326 | 327 | augroup SwapPasteMoveDetect 328 | autocmd! 329 | " Wait an extra CursorMoved event since there always seems to be one fired after this function ends 330 | autocmd CursorMoved autocmd SwapPasteMoveDetect CursorMoved call EndSwapPaste() 331 | augroup END 332 | endfunction 333 | 334 | " Default Paste Behaviour is: 335 | " p - paste after newline if multiline, paste after character if non-multiline 336 | " P - paste before newline if multiline, paste before character if non-multiline 337 | " gp - same as p but keep old cursor position 338 | " gP - same as P but keep old cursor position 339 | " - same as p but does not auto-format 340 | " - same as P but does not auto-format 341 | " g - same as c-p but keeps cursor position 342 | " g - same as c-p but keeps cursor position 343 | function! EasyClip#Paste#SetDefaultMappings() 344 | 345 | let bindings = 346 | \ [ 347 | \ ['p', 'XEasyClipPaste', 'x', 1], 348 | \ ['P', 'XEasyClipPaste', 'x', 1], 349 | \ ['gp', 'XG_EasyClipPaste', 'x', 1], 350 | \ ['gP', 'XG_EasyClipPaste', 'x', 1], 351 | \ ['p', 'XEasyClipPasteUnformatted', 'x', 1], 352 | \ ['P', 'XEasyClipPasteUnformatted', 'x', 1], 353 | \ ['P', 'EasyClipPasteBefore', 'n', 1], 354 | \ ['p', 'EasyClipPasteAfter', 'n', 1], 355 | \ ['gp', 'G_EasyClipPasteAfter', 'n', 1], 356 | \ ['gP', 'G_EasyClipPasteBefore', 'n', 1], 357 | \ ['p', 'EasyClipPasteUnformattedAfter', 'n', 1], 358 | \ ['P', 'EasyClipPasteUnformattedBefore', 'n', 1], 359 | \ ['gp', 'G_EasyClipPasteUnformattedAfter', 'n', 1], 360 | \ ['gP', 'G_EasyClipPasteUnformattedBefore', 'n', 1], 361 | \ ] 362 | 363 | if g:EasyClipUsePasteToggleDefaults 364 | let bindings += [ 365 | \ ['', 'EasyClipSwapPasteForward', 'n', 1], 366 | \ ['', 'EasyClipSwapPasteBackwards', 'n', 1], 367 | \ ] 368 | endif 369 | 370 | for binding in bindings 371 | call call('EasyClip#AddWeakMapping', binding) 372 | endfor 373 | endfunction 374 | 375 | function! EasyClip#Paste#Init() 376 | 377 | if g:EasyClipUsePasteDefaults 378 | call EasyClip#Paste#SetDefaultMappings() 379 | endif 380 | endfunction 381 | -------------------------------------------------------------------------------- /autoload/EasyClip/Shared.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | 3 | " Thanks https://github.com/vim-scripts/YankRing.vim/blob/a884f3a161fa3cd8c996eb53a3d1c68631f60c21/plugin/yankring.vim#L273 4 | let s:newLinePattern = "\2" 5 | let s:newLinePatternRegexp = "\2" 6 | let s:shareYanksFile = '' 7 | let s:mostRecentYanksFileReadTime = 0 8 | 9 | function! EasyClip#Shared#SaveToFileIfDirty() 10 | if !g:EasyClipShareYanks 11 | return 12 | endif 13 | 14 | let l:yankstackStrings = [] 15 | 16 | for yankStackItem in [EasyClip#Yank#GetYankstackHead()] + EasyClip#Yank#GetYankstackTail() 17 | let l:yankstackItemCopy = { 'text': yankStackItem.text, 'type': yankStackItem.type } 18 | let l:yankstackItemCopy.text = substitute(l:yankstackItemCopy.text, "\n", s:newLinePattern, 'g') 19 | call add(l:yankstackStrings, string(l:yankstackItemCopy)) 20 | endfor 21 | 22 | " Thanks https://github.com/xolox/vim-misc/blob/master/autoload/xolox/misc/list.vim 23 | " Remove duplicate values from the given list in-place (preserves order). 24 | call reverse(l:yankstackStrings) 25 | call filter(l:yankstackStrings, 'count(l:yankstackStrings, v:val) == 1') 26 | let l:yankstackStrings = reverse(l:yankstackStrings) 27 | 28 | let fileWriteStatus = writefile(l:yankstackStrings, s:shareYanksFile) 29 | 30 | if fileWriteStatus != 0 31 | echohl ErrorMsg 32 | echo 'Failed to save EasyClip yank stack' 33 | echohl None 34 | return 35 | endif 36 | 37 | let s:mostRecentYanksFileReadTime = getftime(s:shareYanksFile) 38 | endfunction 39 | 40 | function! EasyClip#Shared#LoadFileIfChanged() 41 | if !g:EasyClipShareYanks 42 | return 0 43 | endif 44 | 45 | if !filereadable(s:shareYanksFile) 46 | return 0 47 | endif 48 | 49 | " Only read in yanks from disk if the file has been modified since 50 | " last read 51 | let l:currentYanksFileModificationTime = getftime(s:shareYanksFile) 52 | if l:currentYanksFileModificationTime <= s:mostRecentYanksFileReadTime 53 | return 0 54 | endif 55 | 56 | let s:mostRecentYanksFileReadTime = l:currentYanksFileModificationTime 57 | 58 | let l:allYanksFileContent = readfile(s:shareYanksFile) 59 | let l:allYanks = [] 60 | 61 | for allYanksFileContentLine in l:allYanksFileContent 62 | let l:allYanksItem = eval(allYanksFileContentLine) 63 | let l:allYanksItem.text = substitute(l:allYanksItem.text, s:newLinePatternRegexp, "\n", 'g') 64 | call add(l:allYanks, l:allYanksItem) 65 | endfor 66 | 67 | if len(l:allYanks) 68 | call EasyClip#Yank#SetYankStackHead(remove(l:allYanks, 0)) 69 | call EasyClip#Yank#SetYankStackTail(l:allYanks) 70 | endif 71 | 72 | call EasyClip#Yank#SyncNumberedRegisters() 73 | return 1 74 | endfunction 75 | 76 | function! EasyClip#Shared#Init() 77 | if !g:EasyClipShareYanks 78 | return 79 | endif 80 | 81 | for dir in split(g:EasyClipShareYanksDirectory, ",") 82 | if isdirectory(expand(dir)) 83 | let g:EasyClipShareYanksDirectory = expand(dir) 84 | break 85 | endif 86 | endfor 87 | 88 | let s:shareYanksFile = g:EasyClipShareYanksDirectory . '/' . g:EasyClipShareYanksFile 89 | 90 | let yankHeadBeforeLoad = EasyClip#Yank#GetYankstackHead() 91 | 92 | call EasyClip#Shared#LoadFileIfChanged() 93 | 94 | let newYankHead = EasyClip#Yank#GetYankstackHead() 95 | 96 | " Do not clobber the initial yank after first loading Vim 97 | if yankHeadBeforeLoad.text !=# newYankHead.text 98 | EasyClipBeforeYank 99 | call EasyClip#Yank#SetYankStackHead(yankHeadBeforeLoad) 100 | EasyClipOnYanksChanged 101 | endif 102 | endfunction 103 | -------------------------------------------------------------------------------- /autoload/EasyClip/Substitute.vim: -------------------------------------------------------------------------------- 1 | 2 | """"""""""""""""""""""" 3 | " Variables 4 | """"""""""""""""""""""" 5 | 6 | " This is made global because it's changed in paste.vim 7 | let g:lastSubChangedtick = -1 8 | 9 | let s:activeRegister = EasyClip#GetDefaultReg() 10 | let s:moveCursor = 0 11 | 12 | """"""""""""""""""""""" 13 | " Plugs 14 | """"""""""""""""""""""" 15 | nnoremap SubstituteOverMotionMap :call EasyClip#Substitute#OnPreSubstitute(v:register, 1):set opfunc=EasyClip#Substitute#SubstituteMotiong@ 16 | nnoremap G_SubstituteOverMotionMap :call EasyClip#Substitute#OnPreSubstitute(v:register, 0):set opfunc=EasyClip#Substitute#SubstituteMotiong@ 17 | 18 | nnoremap SubstituteToEndOfLine :call EasyClip#Substitute#SubstituteToEndOfLine(v:register, 1):call repeat#set("\SubstituteToEndOfLine") 19 | nnoremap G_SubstituteToEndOfLine :call EasyClip#Substitute#SubstituteToEndOfLine(v:register, 0):call repeat#set("\G_SubstituteToEndOfLine") 20 | 21 | nnoremap SubstituteLine :call EasyClip#Substitute#SubstituteLine(v:register, v:count):call repeat#set("\SubstituteLine") 22 | 23 | """"""""""""""""""""""" 24 | " Functions 25 | """"""""""""""""""""""" 26 | function! EasyClip#Substitute#OnPreSubstitute(register, moveCursor) 27 | let s:activeRegister = a:register 28 | 29 | " This is necessary to get around a bug in vim where the active register persists to 30 | " the next command. Repro by doing "_d and then a command that uses v:register 31 | if a:register == "_" 32 | let s:activeRegister = EasyClip#GetDefaultReg() 33 | endif 34 | 35 | let s:moveCursor = a:moveCursor 36 | endfunction 37 | 38 | function! EasyClip#Substitute#SubstituteMotion(type, ...) 39 | 40 | let startPos = getpos('.') 41 | 42 | if &selection ==# 'exclusive' 43 | let excl_right = "\" 44 | else 45 | let excl_right = "" 46 | endif 47 | 48 | let oldVirtualEdit=&virtualedit 49 | set virtualedit=onemore 50 | 51 | " use keepjumps since we only want to change jumplist 52 | " if it's multiline 53 | if a:type ==# 'line' 54 | exe "keepjump normal! '[V']".excl_right 55 | elseif a:type ==# 'char' 56 | exe "keepjump normal! `[v`]".excl_right 57 | else 58 | echom "Unexpected selection type" 59 | exec "set virtualedit=". oldVirtualEdit 60 | return 61 | endif 62 | 63 | let reg = s:activeRegister 64 | 65 | if (getreg(reg) =~# "\n") 66 | if s:moveCursor 67 | " Record the start of the substitution to the jump list 68 | exec "normal! m`" 69 | endif 70 | endif 71 | 72 | " Using "c" change doesn't work correctly for multiline, 73 | " Adds an extra line at the end, so delete instead 74 | exe "normal! \"_d" 75 | 76 | " Use our own version of paste so it autoformats and positions the cursor correctly 77 | call EasyClip#Paste#Paste("P", 1, reg, 0) 78 | exec "set virtualedit=". oldVirtualEdit 79 | 80 | let g:lastSubChangedtick = b:changedtick 81 | 82 | if !s:moveCursor 83 | call setpos('.', startPos) 84 | 85 | " For some reason this is necessary otherwise doing gS and then hitting 'j' does not work as you'd expect (jumps to end of next line) 86 | normal! hl 87 | end 88 | endfunction 89 | 90 | function! EasyClip#Substitute#SubstituteLine(reg, count) 91 | 92 | " Check for black hole register to get around a bug in vim where the active 93 | " register persists to the next command 94 | let reg = (a:reg == "_" ? EasyClip#GetDefaultReg() : a:reg) 95 | 96 | if getreg(reg) !~ '\n' 97 | 98 | exec "normal! 0\"_d$" 99 | " Use our own version of paste so it autoformats and positions the cursor correctly 100 | call EasyClip#Paste#Paste("P", 1, reg, 0) 101 | else 102 | let isLastLine = (line(".") == line("$")) 103 | 104 | let cnt = a:count > 0 ? a:count : 1 105 | exe "normal! ". cnt . "\"_dd" 106 | 107 | let i = 0 108 | while i < cnt 109 | " Use our own version of paste so it autoformats and positions the cursor correctly 110 | call EasyClip#Paste#Paste(isLastLine ? "p" : "P", 1, reg, 0) 111 | 112 | let i = i + 1 113 | endwhile 114 | endif 115 | 116 | let g:lastSubChangedtick = b:changedtick 117 | endfunction 118 | 119 | function! EasyClip#Substitute#SubstituteToEndOfLine(reg, moveCursor) 120 | let startPos = getpos('.') 121 | exec "normal! \"_d$" 122 | 123 | " Use our own version of paste so it autoformats and positions the cursor correctly 124 | call EasyClip#Paste#Paste("p", 1, a:reg, 0) 125 | 126 | if !a:moveCursor 127 | call setpos('.', startPos) 128 | endif 129 | endfunction 130 | 131 | function! EasyClip#Substitute#SetDefaultBindings() 132 | 133 | let bindings = 134 | \ [ 135 | \ ['s', 'SubstituteOverMotionMap', 'n', 1], 136 | \ ['gs', 'G_SubstituteOverMotionMap', 'n', 1], 137 | \ ['ss', 'SubstituteLine', 'n', 1], 138 | \ ['s', 'XEasyClipPaste', 'x', 1], 139 | \ ['S', 'SubstituteToEndOfLine', 'n', 1], 140 | \ ['gS', 'G_SubstituteToEndOfLine', 'n', 1], 141 | \ ] 142 | 143 | for binding in bindings 144 | call call("EasyClip#AddWeakMapping", binding) 145 | endfor 146 | endfunction 147 | 148 | function! EasyClip#Substitute#Init() 149 | 150 | if g:EasyClipUseSubstituteDefaults 151 | call EasyClip#Substitute#SetDefaultBindings() 152 | endif 153 | endfunction 154 | -------------------------------------------------------------------------------- /autoload/EasyClip/Yank.vim: -------------------------------------------------------------------------------- 1 | scriptencoding utf-8 2 | 3 | " A lot of this is based on yankstack by Max Brunsfeld 4 | " See original code here: https://github.com/maxbrunsfeld/vim-yankstack 5 | 6 | """"""""""""""""""""""" 7 | " Variables 8 | """"""""""""""""""""""" 9 | let s:activeRegister = EasyClip#GetDefaultReg() 10 | let s:yankstackTail = [] 11 | let s:ignoreNextYank = 0 12 | let s:preYankPos = [] 13 | let s:yankCount = 0 14 | let s:preYankWinView = {} 15 | 16 | """"""""""""""""""""""" 17 | " Plugs 18 | """"""""""""""""""""""" 19 | 20 | nnoremap YankMotionEndOfLinePlug :EasyClipBeforeYanky$:EasyClipOnYanksChanged 21 | 22 | nnoremap EasyClipRotateYanksForward :call EasyClip#Yank#ManuallyRotateYanks(1) 23 | nnoremap EasyClipRotateYanksBackward :call EasyClip#Yank#ManuallyRotateYanks(-1) 24 | 25 | nnoremap YankLinePreserveCursorPosition :call EasyClip#Yank#PreYankMotion():call EasyClip#Yank#YankLine() 26 | nnoremap YankPreserveCursorPosition ":call EasyClip#Yank#PreYankMotion():set opfunc=EasyClip#Yank#YankMotion" . (v:count > 0 ? v:count : '') . "g@" 27 | 28 | xnoremap VisualModeYank :call VisualModeYank(v:register) 29 | 30 | """"""""""""""""""""""" 31 | " Functions 32 | """"""""""""""""""""""" 33 | function! s:VisualModeYank(reg) 34 | if a:reg == EasyClip#GetDefaultReg() || g:EasyClipCopyExplicitRegisterToDefault 35 | EasyClipBeforeYank 36 | normal! gvy 37 | EasyClipOnYanksChanged 38 | 39 | if g:EasyClipCopyExplicitRegisterToDefault 40 | call EasyClip#Yank#SetRegToYankInfo(a:reg, EasyClip#Yank#GetYankstackHead()) 41 | endif 42 | else 43 | let oldDefaultInfo = EasyClip#Yank#GetYankstackHead() 44 | " If register is specified explicitly then do not change default register 45 | " or add to yank history 46 | exec "normal! gv\"" . a:reg . "y" 47 | call EasyClip#Yank#SetYankStackHead(oldDefaultInfo) 48 | endif 49 | 50 | if g:EasyClipPreserveCursorPositionAfterYank 51 | execute "normal! gv\" 52 | endif 53 | endfunction 54 | 55 | function! EasyClip#Yank#OnBeforeYank() 56 | call EasyClip#Shared#LoadFileIfChanged() 57 | 58 | if s:ignoreNextYank 59 | let s:ignoreNextYank = 0 60 | return 61 | endif 62 | 63 | let head = EasyClip#Yank#GetYankstackHead() 64 | call s:AddToTail(head) 65 | endfunction 66 | 67 | function! s:AddToTail(entry) 68 | if !empty(a:entry.text) && (empty(s:yankstackTail) || (a:entry != s:yankstackTail[0])) 69 | call insert(s:yankstackTail, a:entry) 70 | let s:yankstackTail = s:yankstackTail[: g:EasyClipYankHistorySize-1] 71 | endif 72 | endfunction 73 | 74 | function! EasyClip#Yank#SyncNumberedRegisters() 75 | for i in range(1, min([len(s:yankstackTail), 9])) 76 | let entry = s:yankstackTail[i-1] 77 | 78 | call setreg(i, entry.text, entry.type) 79 | endfor 80 | endfunction 81 | 82 | function! EasyClip#Yank#OnYanksChanged() 83 | 84 | call EasyClip#Yank#SyncNumberedRegisters() 85 | call EasyClip#Shared#SaveToFileIfDirty() 86 | endfunction 87 | 88 | function! EasyClip#Yank#Rotate(offset) 89 | 90 | call EasyClip#Shared#LoadFileIfChanged() 91 | 92 | if empty(s:yankstackTail) 93 | return 94 | endif 95 | 96 | let offset_left = a:offset 97 | while offset_left != 0 98 | 99 | let head = EasyClip#Yank#GetYankstackHead() 100 | 101 | if offset_left > 0 102 | let l:entry = remove(s:yankstackTail, 0) 103 | call add(s:yankstackTail, head) 104 | let offset_left -= 1 105 | elseif offset_left < 0 106 | let l:entry = remove(s:yankstackTail, -1) 107 | call insert(s:yankstackTail, head) 108 | let offset_left += 1 109 | endif 110 | 111 | call EasyClip#Yank#SetYankStackHead(l:entry) 112 | endwhile 113 | 114 | EasyClipOnYanksChanged 115 | endfunction 116 | 117 | function! EasyClip#Yank#GetNumYanks() 118 | return 1 + len(s:yankstackTail) 119 | endfunction 120 | 121 | function! EasyClip#Yank#ClearYanks() 122 | call EasyClip#Shared#LoadFileIfChanged() 123 | 124 | let l:size = len(s:yankstackTail) 125 | 126 | let s:yankstackTail = [] 127 | let s:ignoreNextYank = 1 128 | EasyClipOnYanksChanged 129 | 130 | echo "Cleared yank history of " . l:size . " entries" 131 | endfunction 132 | 133 | function! EasyClip#Yank#GetYankstackHead() 134 | return EasyClip#Yank#GetYankInfoForReg(EasyClip#GetDefaultReg()) 135 | endfunction 136 | 137 | function! EasyClip#Yank#GetYankInfoForReg(reg) 138 | return { 'text': getreg(a:reg), 'type': getregtype(a:reg) } 139 | endfunction 140 | 141 | function! EasyClip#Yank#GetYankstackTail() 142 | return s:yankstackTail 143 | endfunction 144 | 145 | function! EasyClip#Yank#SetYankStackHead(entry) 146 | call EasyClip#Yank#SetRegToYankInfo(EasyClip#GetDefaultReg(), a:entry) 147 | endfunction 148 | 149 | function! EasyClip#Yank#SetRegToYankInfo(reg, entry) 150 | call setreg(a:reg, a:entry.text, a:entry.type) 151 | endfunction 152 | 153 | function! EasyClip#Yank#SetYankStackTail(tail) 154 | let s:yankstackTail = a:tail 155 | endfunction 156 | 157 | function! EasyClip#Yank#ShowYanks() 158 | echohl WarningMsg | echo "--- Yanks ---" | echohl None 159 | let i = 0 160 | for yank in EasyClip#Yank#EasyClipGetAllYanks() 161 | call EasyClip#Yank#ShowYank(yank, i) 162 | let i += 1 163 | endfor 164 | endfunction 165 | 166 | function! EasyClip#Yank#GetYankInfoForIndex(index) 167 | if a:index < 0 168 | throw "Invalid index given to EasyClip" 169 | endif 170 | 171 | if a:index == 0 172 | return EasyClip#Yank#GetYankstackHead() 173 | endif 174 | 175 | let tailIndex = a:index - 1 176 | 177 | if tailIndex >= len(s:yankstackTail) 178 | throw "Invalid index given to EasyClip" 179 | endif 180 | 181 | return s:yankstackTail[tailIndex] 182 | endfunction 183 | 184 | function! EasyClip#Yank#ShowYank(yank, index) 185 | let index = printf("%-4d", a:index) 186 | let line = substitute(a:yank.text, '\V\n', '^M', 'g') 187 | 188 | if len(line) > g:EasyClipShowYanksWidth 189 | let line = line[: g:EasyClipShowYanksWidth] . '…' 190 | endif 191 | 192 | echohl Directory | echo index 193 | echohl None | echon line 194 | echohl None 195 | endfunction 196 | 197 | function! EasyClip#Yank#PreYankMotion() 198 | let s:yankCount = v:count > 0 ? v:count : 1 199 | let s:activeRegister = v:register 200 | 201 | " This is necessary to get around a bug in vim where the active register persists to 202 | " the next command. Repro by doing "_d and then a command that uses v:register 203 | if s:activeRegister ==# "_" 204 | let s:activeRegister = EasyClip#GetDefaultReg() 205 | endif 206 | 207 | let s:preYankPos = getpos('.') 208 | let s:preYankWinView = winsaveview() 209 | endfunction 210 | 211 | function! EasyClip#Yank#_YankLastChangedText(type, reg) 212 | 213 | if &selection ==# 'exclusive' 214 | let excl_right = "\" 215 | else 216 | let excl_right = "" 217 | endif 218 | 219 | if a:type !=# 'line' && a:type !=# 'char' 220 | echoerr "Unexpected selection type '" . a:type . "'" 221 | return 222 | endif 223 | 224 | let oldDefaultRegInfo = EasyClip#Yank#GetYankstackHead() 225 | 226 | let destinationReg = a:reg 227 | 228 | if g:EasyClipCopyExplicitRegisterToDefault 229 | let destinationReg = EasyClip#GetDefaultReg() 230 | endif 231 | 232 | if destinationReg ==# EasyClip#GetDefaultReg() 233 | EasyClipBeforeYank 234 | endif 235 | 236 | exe "keepjumps normal! `[" . (a:type ==# 'line' ? 'V' : 'v') 237 | \ . "`]".excl_right."\"".destinationReg."y" 238 | 239 | if destinationReg ==# EasyClip#GetDefaultReg() 240 | EasyClipOnYanksChanged 241 | endif 242 | 243 | if g:EasyClipCopyExplicitRegisterToDefault && a:reg !=# EasyClip#GetDefaultReg() 244 | call EasyClip#Yank#SetRegToYankInfo(a:reg, EasyClip#Yank#GetYankstackHead()) 245 | endif 246 | 247 | " When clipboard is set to '' then it clobbers default register even when an 248 | " explicit register is used so restore that 249 | if !g:EasyClipCopyExplicitRegisterToDefault && a:reg !=# EasyClip#GetDefaultReg() 250 | call EasyClip#Yank#SetYankStackHead(oldDefaultRegInfo) 251 | endif 252 | endfunction 253 | 254 | function! EasyClip#Yank#YankMotion(type) 255 | 256 | let oldVisualStart = getpos("'<") 257 | let oldVisualEnd = getpos("'>") 258 | 259 | call EasyClip#Yank#_YankLastChangedText(a:type, s:activeRegister) 260 | 261 | call setpos("'<", oldVisualStart) 262 | call setpos("'>", oldVisualEnd) 263 | 264 | if g:EasyClipPreserveCursorPositionAfterYank && !empty(s:preYankWinView) 265 | 266 | call winrestview(s:preYankWinView) 267 | let s:preYankWinView = {} 268 | 269 | " This is necessary for some reason otherwise if you go down a line it will 270 | " jump to the column where the yank normally positions the cursor by default 271 | " To repro just remove this line, run yiq inside quotes, then go down a line 272 | if col('.') == col('$')-1 273 | normal! hl 274 | else 275 | normal! lh 276 | endif 277 | endif 278 | endfunction 279 | 280 | function! EasyClip#Yank#YankLine() 281 | EasyClipBeforeYank 282 | exec 'normal! '. s:yankCount . '"'. s:activeRegister .'yy' 283 | 284 | call setpos('.', s:preYankPos) 285 | EasyClipOnYanksChanged 286 | endfunction 287 | 288 | function! EasyClip#Yank#EasyClipGetAllYanks() 289 | call EasyClip#Shared#LoadFileIfChanged() 290 | return [EasyClip#Yank#GetYankstackHead()] + s:yankstackTail 291 | endfunction 292 | 293 | function! EasyClip#Yank#ManuallyRotateYanks(offset) 294 | 295 | call EasyClip#Yank#Rotate(a:offset) 296 | 297 | let lines = split(EasyClip#Yank#GetYankstackHead().text, '\n') 298 | 299 | if empty(lines) 300 | " This happens when it only contains newlines 301 | echo "Current Yank: " 302 | else 303 | echo "Current Yank: " . lines[0] . "..." 304 | endif 305 | endfunction 306 | 307 | function! EasyClip#Yank#SetDefaultMappings() 308 | 309 | let bindings = 310 | \ [ 311 | \ ['Y', ':EasyClipBeforeYanky$:EasyClipOnYanksChanged', 'n', 0], 312 | \ ['y', 'YankPreserveCursorPosition', 'n', 1], 313 | \ ['yy', 'YankLinePreserveCursorPosition', 'n', 1], 314 | \ ['y', 'VisualModeYank', 'x', 1], 315 | \ ] 316 | 317 | " Let the user set [y themselves so that we don't conflict with vim-unimpaired 318 | "\ ['[y', 'EasyClipRotateYanksForward', 'n', 1], 319 | "\ [']y', 'EasyClipRotateYanksBackward', 'n', 1], 320 | 321 | for binding in bindings 322 | call call("EasyClip#AddWeakMapping", binding) 323 | endfor 324 | endfunction 325 | 326 | function! EasyClip#Yank#OnFocusLost() 327 | " It is tempting to call EasyClip#Shared#SaveToFileIfDirty here instead of every time the yank buffer 328 | " changes but we can't do this since this event doesn't fire quick enough (and often fires after OnFocusGained 329 | " has already fired on the other vim instance) 330 | 331 | if EasyClip#GetDefaultReg() ==# '*' 332 | let s:yankHeadBeforeFocusLost = EasyClip#Yank#GetYankstackHead() 333 | endif 334 | endfunction 335 | 336 | function! EasyClip#Yank#OnFocusGained() 337 | let didLoad = EasyClip#Shared#LoadFileIfChanged() 338 | 339 | " If we are using the system register as our yank head, 340 | " then we have to make sure that it doesn't get clobbered 341 | " when the user leaves and then returns to vim 342 | " To do this, we check if the yank head before leaving is 343 | " the same as when it returns, and if not add a new entry properly 344 | " for the previous yank head 345 | if EasyClip#GetDefaultReg() ==# '*' && exists("s:yankHeadBeforeFocusLost") 346 | 347 | " Ignore the system clipboard if we just replaced the entire clipboard above 348 | " since our cached yank stack head is no longer valid 349 | if !didLoad 350 | let newYankHead = @* 351 | 352 | " If the clipboard contains binary information then 'newYankHead' will be empty 353 | " Restore old yank in this case 354 | if s:yankHeadBeforeFocusLost.text !=# newYankHead 355 | " User copied something externally 356 | call s:AddToTail(s:yankHeadBeforeFocusLost) 357 | EasyClipOnYanksChanged 358 | endif 359 | endif 360 | 361 | unlet s:yankHeadBeforeFocusLost 362 | endif 363 | endfunction 364 | 365 | function! EasyClip#Yank#Init() 366 | 367 | if g:EasyClipUseYankDefaults 368 | call EasyClip#Yank#SetDefaultMappings() 369 | endif 370 | 371 | " Watch focus to keep the shared clipboard in sync for use by other 372 | " vim sessions 373 | augroup _easyclip_focuswatch 374 | au! 375 | autocmd FocusGained * call EasyClip#Yank#OnFocusGained() 376 | autocmd FocusLost * call EasyClip#Yank#OnFocusLost() 377 | augroup END 378 | endfunction 379 | -------------------------------------------------------------------------------- /doc/easyclip.txt: -------------------------------------------------------------------------------- 1 | *easyclip.txt* Simplified clipboard functionality for Vim. 2 | *easyclip* 3 | *easy-clip* 4 | 5 | Author: Steve Vermeulen , based on work by Max 6 | Brunsfeld 7 | 8 | |easyclip-introduction| Introduction 9 | |easyclip-installation| Installation 10 | |easyclip-cut| Black hole redirection / cut 11 | |easyclip-substitute| Substitute operator 12 | |easyclip-yankbuffer| Yank Buffer 13 | |easyclip-paste| Paste 14 | |easyclip-shared-yank-history| Shared Yank History 15 | |easyclip-clipboard-setting| Clipboard Setting 16 | |easyclip-options| Options 17 | |easyclip-mappings| Mappings 18 | |easyclip-custom-yanks| Custom Yanks 19 | |easyclip-feedback| Feedback 20 | |easyclip-changelog| Changelog 21 | |easyclip-license| License 22 | 23 | INTRODUCTION *easyclip-introduction* 24 | 25 | Author: Steve Vermeulen (https://github.com/svermeulen), based on work by 26 | Max Brunsfeld (http://www.github.com/maxbrunsfeld) 27 | 28 | EasyClip is a plugin for Vim which contains a collection of clipboard related 29 | functionality with the goal of making using Vim simpler and more intuitive 30 | without losing any of its power. 31 | 32 | A good starting point for the motivation behind this plugin can be found in Drew 33 | Neil's post: 34 | Registers: The Good, the Bad, and the Ugly Parts 35 | (http://vimcasts.org/blog/2013/11/registers-the-good-the-bad-and-the-ugly-parts/) 36 | 37 | The most recent version of this plugin can be found at 38 | https://github.com/svermeulen/vim-easyclip 39 | 40 | INSTALLATION *easyclip-installation* 41 | 42 | I recommend loading your plugins with neobundle 43 | (https://github.com/Shougo/neobundle.vim) or vundle 44 | (https://github.com/gmarik/vundle) or pathogen 45 | (https://github.com/tpope/vim-pathogen) 46 | 47 | This plugin also requires that you have Tim Pope's repeat.vim 48 | (https://github.com/tpope/vim-repeat) plugin installed. 49 | 50 | BLACK HOLE REDIRECTION *easyclip-cut* 51 | 52 | By default, Vim's built-in delete operator will yank the deleted text in 53 | addition to just deleting it. This works great when you want to cut text and 54 | paste it somewhere else, but in many other cases it can make things more 55 | difficult. For example, if you want to make some tiny edit to fix formatting 56 | after cutting some text, you either have to have had the foresight to use a 57 | named register, or specify the black hole register explicitly to do your 58 | formatting. This plugin solves that problem by redirecting all change and 59 | delete operations to the black hole register and introducing a new operator, ' 60 | cut' (by default this is mapped to the `m` key for 'move'). 61 | 62 | There is simply no need to clutter up the yank history with every single edit, 63 | when you almost always know at the time you are deleting text whether it's 64 | something that is worth keeping around or not. 65 | 66 | **NOTE** As a result of the above, by default easyclip will shadow an import 67 | vim function: The Add Mark key (`m`). Therefore either you will want to use a 68 | different key for the 'cut' operator (see options section below for this) or 69 | remap something else to 'add mark'. For example, to use `gm` for 'add mark' 70 | instead of `m`, include the following in your vimrc: 71 | 72 | nnoremap gm m 73 | 74 | SUBSTITUTION OPERATOR *easyclip-substitute* 75 | 76 | Because replacing text is such a common operation, EasyClip includes a motion 77 | for it. It is essentially equivalent to doing a change operation then pasting 78 | using the specified register. For example, assuming you have mapped this 79 | motion to the `s` key, to paste over the word under the cursor you would type ` 80 | siw`, or to paste inside brackets, `si(`, etc. 81 | 82 | It can also take a register to use for the substitution (eg. `"asip`), and is 83 | fully repeatable using the `.` key. 84 | 85 | **NOTE** This feature is off by default. To use, you have to either enable 86 | the option |g:EasyClipUseSubstituteDefaults| (in which case it will be mapped 87 | to the `s` key) or map the key/keys of your choice to the `` 88 | mappings found in substitute.vim. 89 | 90 | YANK BUFFER *easyclip-yankbuffer* 91 | 92 | EasyClip allows you to yank and cut things without worrying about losing text 93 | that you copied previously. It achieves this by storing all yanks into a 94 | buffer, which you can cycle through forward or backwards to choose the yank 95 | that you want 96 | 97 | This works very similar to the way YankRing (https://github.com/vim-scripts/ 98 | YankRing.vim) and YankStack (https://github.com/maxbrunsfeld/vim-yankstack) 99 | work, in that you can use a key binding to toggle between different yanks 100 | immediately after triggering a paste or substitute. (Most of the 101 | functionality is actually taken and adapted from Yankstack, with changes to 102 | make it work with substitute) 103 | 104 | By default, the keys to toggle the paste are mapped to `` and `` ( 105 | similar to yankring). For example, executing `p` will paste, then toggle 106 | it to the most recent yank before that. You can continue toggling forwards/ 107 | backwards in the yank history to replace the most recent paste as much as you 108 | want. Note that the toggle action will of course not be included in the undo 109 | history. That is, pressing undo after any number of swaps will undo the paste 110 | and not each swap. 111 | 112 | This method of toggling the chosen yank after paste will probably be your 113 | primary method of digging back into the yank buffer. Note that in this case 114 | the yank buffer is unchanged. What this means for example is that you can 115 | toggle a given paste back using `` 10 times, then if you perform a new 116 | paste in a different location it will still use the most recent yank (and not 117 | the final yank you arrived at after 10 swaps). 118 | 119 | Alternatively, you can execute keys `[y` or `]y` to navigate the yank buffer 120 | 'head' forwards or backwards. In this case the change will be permanent. 121 | That is, pressing `[y[yp` will paste the third most recent yank. 122 | Subsequent pastes will use the same yank, until you go forwards again using `]y`. 123 | 124 | The [y and ]y mappings are not on by default (map them manually). 125 | 126 | You can view the full list of yanks at any time by running the command `:Yanks` 127 | 128 | Note that you can swap substitution operations in the same way as paste. 129 | 130 | Every time the yank buffer changes, it also populates all the numbered 131 | registers. `"1` is therefore the previous yank, `"2` is the yank before that, 132 | etc. This is similar to how the numbered registers work by default (but a bit 133 | more sane). (Credit to Drew Neil for the suggestion) 134 | 135 | Also, see |g:EasyClipPreserveCursorPositionAfterYank| option below for an 136 | optional non standard customization to yank 137 | 138 | PASTE *easyclip-paste* 139 | 140 | By default EasyClip preserves the default vim paste behaviour, which is the 141 | following: 142 | 143 | * `p` (lowercase) pastes text after the current line if the pasted text is 144 | multiline (or after the current character if non-multiline) 145 | * `P` (uppercase) behaves the same except acts before the current line (or 146 | before the current character if non-multiline) 147 | 148 | When the text is multi-line, the cursor is placed at the start of the new text. 149 | When the paste is non-multiline, the cursor is placed at the end. 150 | 151 | Alternatively, you can enable the option 152 | |g:EasyClipAlwaysMoveCursorToEndOfPaste| to have the cursor positioned at the 153 | end in both cases (off by default). Note that when this option is enabled, 154 | the beginning of the multi-line text is added to the jumplist, so you can 155 | still return to the start of the paste by pressing `` (and this applies 156 | to multi-line substitutions as well) 157 | 158 | Another non-standard option is |g:EasyClipAutoFormat| (off by default), which 159 | will automatically format text immediately after it is pasted. This can be 160 | useful when pasting text from one indent level to another. 161 | 162 | Easy Clip also includes a mapping for insert mode paste, which automatically 163 | turns on 'paste' mode for the duration of the paste. Using 'paste' mode will 164 | work much more intuitively when pasting text with multiple lines while in 165 | insert mode. You can enable this by including something similar to the 166 | following in your .vimrc: 167 | 168 | imap EasyClipInsertModePaste 169 | 170 | For convenience, there is also a plug for command mode paste, which you can 171 | enable with the following: 172 | 173 | cmap EasyClipCommandModePaste 174 | 175 | SHARED YANK HISTORY *easyclip-shared-yank-history* 176 | 177 | EasyClip can automatically store the yank history to file, so that it can be 178 | restored the next time you start Vim. Storing it to file also allows other 179 | active Vim instances to seamlessly share the same clipboard and yank history. 180 | 181 | You can enable this feature by enabling the option |g:EasyClipShareYanks| 182 | (NOTE: off by default). You can also customize where the yank history file 183 | gets stored (see options section below) 184 | 185 | CLIPBOARD SETTING *easyclip-clipboard-setting* 186 | 187 | Vim's built-in setting for |clipboard| can be set to one of the following: 188 | 189 | 1. set clipboard= 190 | 1. set clipboard=unnamed 191 | 1. set clipboard=unnamed,unnamedplus 192 | 1. set clipboard=unnamedplus 193 | 194 | Leaving it as (1) which is Vim's default, will cause all yank/delete/paste 195 | operations to use the `"` register. The only drawback here is that whenever 196 | you want to copy/paste something from another application, you have to 197 | explicitly access the system clipboard, which is represented by the `*` 198 | register. For example, to copy the current line to the system clipboard, you 199 | would type `"*yy`. And to paste some text copied from another window, you 200 | would type `"*p` 201 | 202 | To avoid this extra work, you can use option (2) and set it to `unnamed`. 203 | This will cause all yank/delete/paste operations to use the system register 204 | `*`. This way, you can copy something in Vim then immediately paste it into 205 | another application. And vice versa when returning to vim. 206 | 207 | I recommend using one of these two options. I personally use option (2). 208 | 209 | When option (3) is enabled, both Vim and EasyClip will use the `+` register as 210 | its default. 211 | 212 | Option (4) is the same as option (3), except Vim will also automatically copy 213 | the contents of the `+` register to the `*` register. 214 | 215 | OPTIONS *easyclip-options* 216 | 217 | EasyClip can be easily customized to whatever mapping you wish, using the 218 | following options: 219 | 220 | *g:EasyClipAutoFormat* Default: 0. Set this to 1 to enable 221 | auto-formatting. This can be useful when pasting text from one indent level to 222 | another. 223 | 224 | *g:EasyClipYankHistorySize* Default: 50. Change this to limit yank history 225 | 226 | *g:EasyClipCopyExplicitRegisterToDefault* Default: 0. When set to 0, easy-clip will not change the default register clipboard when an explicit register is given. For example, when set to 0, if you type `"ayip` it will copy the current paragraph to the `a` register, but it will not affect the default register, so typing `p` will work the same as it did before the above command. When set to 1, typing `"ayip` will copy the paragraph to both. 227 | 228 | *g:EasyClipAlwaysMoveCursorToEndOfPaste* Default: 0. Set this to 1 to always 229 | position cursor at the end of the pasted text for both multi-line and 230 | non-multiline pastes. 231 | 232 | *g:EasyClipPreserveCursorPositionAfterYank* - Default 0 (ie. disabled). Vim's 233 | default behaviour is to position the cursor at the beginning of the yanked text, 234 | which is consistent with other motions. However if you prefer the 235 | cursor position to remain unchanged when performing yanks, enable this 236 | option. 237 | 238 | *g:EasyClipShareYanks* - Default: 0 (ie. disabled). When enabled, yank history 239 | is saved to file, which allows other concurrent Vim instances to automatically 240 | share the yank history, and also allows yank history to be automatically 241 | restored when restarting vim. 242 | 243 | *g:EasyClipShareYanksFile* - Default: '.easyclip'. The name of the file to 244 | save the yank history to when |g:EasyClipShareYanks| is enabled. 245 | 246 | *g:EasyClipShareYanksDirectory* - Default: '$HOME'. The directory to use to 247 | store the file with name given by |g:EasyClipShareYanksFile| setting. Only 248 | applicable when |g:EasyClipShareYanks| option is enabled. 249 | 250 | *g:EasyClipShowYanksWidth* - Default: 80 - The width to display for each line 251 | when the `Yanks` command is executed 252 | 253 | *g:EasyClipSwapPasteForwardFallback* - Default: undefined. If set, and the 254 | previous action was not a paste EasyClipSwapPasteForward will exec 255 | this. Useful if you have muscle memory for a previous use of . 256 | 257 | *g:EasyClipSwapPasteBackwardsFallback* - Default: undefined. If set, and the 258 | previous action was not a paste EasyClipSwapPasteBackwards will exec 259 | this. Useful if you have muscle memory for a previous use of . 260 | 261 | You can also disable the default mappings by setting one or more of the 262 | following to zero. By default they are set to 1 (ie. enabled) 263 | 264 | *g:EasyClipUseYankDefaults* 265 | 266 | *g:EasyClipUseCutDefaults* 267 | 268 | *g:EasyClipUsePasteDefaults* 269 | 270 | *g:EasyClipEnableBlackHoleRedirect* 271 | 272 | *g:EasyClipUsePasteToggleDefaults* 273 | 274 | One exception to the above is substitute, which is 0 by default (ie. disabled) 275 | 276 | *g:EasyClipUseSubstituteDefaults* 277 | 278 | To change from the default mappings, you can disable one of the options above 279 | and then map to the specific mappings of your choice. For example, to 280 | change the mapping for cut (by default set to `m`) to `x`, include the following 281 | in your vimrc:` 282 | 283 | let g:EasyClipUseCutDefaults = 0 284 | 285 | nmap x MoveMotionPlug xmap x MoveMotionXPlug nmap xx 286 | MoveMotionLinePlug 287 | 288 | Or to change the bindings for toggling paste from '' and '' to '' 289 | and '' include the following: 290 | 291 | let g:EasyClipUsePasteToggleDefaults = 0 292 | 293 | nmap EasyClipSwapPasteForward nmap 294 | EasyClipSwapPasteBackwards 295 | 296 | Or to use 'gs' for substitute include the following: (in this case you don't 297 | need to turn off the default since the default is already disabled) 298 | 299 | nmap gs SubstituteOverMotionMap nmap gss SubstituteLine 300 | xmap gs XEasyClipPaste 301 | 302 | For reference, or other kinds of mappings, see the bottom of the file with the 303 | name of the operation you wish to remap (vim-easy-clip/autoload/substitute.vim / 304 | move.vim / yank.vim /etc.) 305 | 306 | Note that EasyClip will only enable a default mapping if it hasn't already been 307 | mapped to something in your .vimrc. 308 | 309 | KEY MAPPINGS *easyclip-mappings* 310 | 311 | *d* - Delete over the given motion and *do not* change clipboard 312 | 313 | *dd* - Delete the line and *do not* change clipboard 314 | 315 | *D* - Delete from cursor to the end of the line and *do not* change clipboard 316 | 317 | *dD* - Delete the contents of line except the newline character (that is, make it blank) and *do not* change clipboard 318 | 319 | *x* - Delete the character under cursor and *do not* change clipboard 320 | 321 | *s* - Delete the character under cursor then enter insert mode and *do not* change clipboard 322 | 323 | *S* - Delete the line under cursor then enter insert mode and *do not* change clipboard 324 | 325 | *c* - Enter insert mode over top the given area and *do not* change clipboard 326 | 327 | *cc* - Enter insert mode over top the current line and *do not* change clipboard 328 | 329 | *C* - Enter insert mode from cursor to the end of the line and *do not* change clipboard 330 | 331 | *p* - Paste from specified register. Inserts after current line if text is multiline, after current character if text is non-multiline. Leaves cursor at end of pasted text. 332 | 333 | *P* - Same as p except inserts text before current line/character 334 | 335 | *p* - Same as 'p' except does not auto-format text. This is only relevant if the auto-format option is enabled 336 | 337 | *P* - Same as 'P' except does not auto-format text. This is only relevant if the auto-format option is enabled 338 | 339 | *gp* - Same as p but preserves the current cursor position 340 | 341 | *gP* - Same as P but preserves the current cursor position 342 | 343 | *gP* - Same as 'P' but preserves the current cursor position 344 | 345 | *gp* - Same as 'p' but preserves the current cursor position 346 | 347 | *m* - Delete over the given motion and copy text to clipboard 348 | 349 | *mm* - Delete the current line and copy text to clipboard 350 | 351 | ** -p>' - Rotate the previous paste forward in yank buffer. Note that this binding will only work if executed immediately after a paste 352 | 353 | ** -n>' - Rotate the previous paste backward in yank buffer. Note that this binding will only work if executed immediately after a paste 354 | 355 | *Y* - Copy text from cursor position to the end of line to the clipboard 356 | 357 | When the option |g:EasyClipUseSubstituteDefaults| is enabled, the following mappings are added: 358 | 359 | s - Substitute over the given motion with specified register (or default register if unspecified). 360 | 361 | ss - Substitute over the current line with specified register (or default register if unspecified). 362 | 363 | gs - Same as s but preserves the current cursor position. 364 | 365 | CUSTOM YANKS *easyclip-custom-yanks* 366 | 367 | If you have custom yanks that occur in your vimrc or elsewhere and would like 368 | them to be included in the yank history, you should call the EasyClip#Yank(). 369 | For example, to add a binding to yank the current file name you could add the 370 | following to your .vimrc: 371 | 372 | nnoremap yf :call EasyClip#Yank(expand('%')) 373 | 374 | Another way to do the above (which is necessary if you don't control the yank 375 | yourself), is to do the following: 376 | 377 | nnoremap yf :EasyClipBeforeYank:let @*=expand('%'):EasyClipOnYanksChanged 378 | 379 | FEEDBACK *easyclip-feedback* 380 | 381 | Feel free to email all feedback/criticism/suggestions to sfvermeulen@gmail.com. 382 | Or, feel free to create a github issue. 383 | 384 | CHANGELOG *easyclip-changelog* 385 | 386 | 2.2 (2015-01-27) 387 | - Bug fixes 388 | - Removed the 'system sync' option since using unnamed register is sufficient for this 389 | - Added support for persistent/shared yanks 390 | 391 | 2.1 (2013-12-06) 392 | - Bug fixes 393 | - Disabled substitution operator by default 394 | 395 | 2.0 (2013-09-22) 396 | - Many bug fixes 397 | - Yankring/Yankstack style post-paste swap 398 | - RSPEC unit tests added for stability 399 | 400 | 1.2 (2013-09-22) 401 | - More bug fixes 402 | 403 | 1.1 (2013-09-03) 404 | - Bunch of bug fixes 405 | 406 | 1.0 (2013-07-08) 407 | - Initial release 408 | 409 | *easyclip-license* 410 | Distributed under the same terms as Vim itself. 411 | See |license|. 412 | 413 | vim:tw=78:ts=8:ft=help:norl: 414 | -------------------------------------------------------------------------------- /plugin/EasyClip.vim: -------------------------------------------------------------------------------- 1 | if exists('g:loaded_EasyClip') 2 | finish 3 | endif 4 | let g:loaded_EasyClip = 1 5 | 6 | call EasyClip#Init() 7 | -------------------------------------------------------------------------------- /spec/easyclip/easyclip_spec.rb: -------------------------------------------------------------------------------- 1 | require "spec_helper" 2 | 3 | describe "EasyClip" do 4 | 5 | before(:all) do 6 | @vim = Vimbot::Driver.new 7 | @vim.start 8 | 9 | @vim.set "visualbell" 10 | @vim.set "noerrorbells" 11 | 12 | @vim.set "runtimepath+=#{PLUGIN_ROOT}" 13 | 14 | @vim.command("let g:EasyClipEnableBlackHoleRedirect = 1") 15 | @vim.command("let g:EasyClipUseCutDefaults = 1") 16 | @vim.command("let g:EasyClipUseSubstituteDefaults = 1") 17 | @vim.command("let g:EasyClipUsePasteDefaults = 1") 18 | @vim.command("let g:EasyClipUsePasteToggleDefaults = 1") 19 | @vim.command("let g:EasyClipUseYankDefaults = 1") 20 | @vim.command("let g:EasyClipAutoFormat = 1") 21 | 22 | @vim.runtime "plugin/easyclip.vim" 23 | 24 | @vim.command("cmap EasyClipCommandModeSwapPasteForward") 25 | @vim.command("cmap EasyClipCommandModeSwapPasteBackward") 26 | 27 | @vim.command("imap EasyClipInsertModeSwapPasteForward") 28 | @vim.command("imap EasyClipInsertModeSwapPasteBackwards") 29 | 30 | @vim.source VIM_REPEAT_PATH 31 | end 32 | 33 | after(:all) { @vim.stop } 34 | 35 | def ClearYanks 36 | @vim.command("ClearYanks") 37 | end 38 | 39 | before(:each) do 40 | @vim.clear_buffer 41 | ClearYanks() 42 | end 43 | 44 | def AddExampleText 45 | @vim.insert "first", "second", "third", "fourth" 46 | @vim.normal "o", "" 47 | end 48 | 49 | def AddExampleYanks 50 | @vim.command("call EasyClip#Yank('one')") 51 | @vim.command("call EasyClip#Yank('two')") 52 | @vim.command("call EasyClip#Yank('three')") 53 | @vim.command("call EasyClip#Yank('four')") 54 | end 55 | 56 | def LinesAreUnchanged 57 | 58 | @vim.normal "gg" 59 | @vim.line.should == "first" 60 | @vim.normal "j" 61 | @vim.line.should == "second" 62 | @vim.normal "j" 63 | @vim.line.should == "third" 64 | @vim.normal "j" 65 | @vim.line.should == "fourth" 66 | end 67 | 68 | def YankExampleText 69 | @vim.normal "gg" 70 | @vim.normal 'yw', 'j', 'yw', 'j', 'yw', 'j', 'yw', 'j' 71 | end 72 | 73 | ################### 74 | # Black hole 75 | ################### 76 | shared_examples "black hole redirection" do 77 | 78 | before do 79 | @vim.clear_buffer 80 | ClearYanks() 81 | 82 | AddExampleText() 83 | 84 | @vim.insert "saved" 85 | @vim.normal "0mw" 86 | @vim.normal "gg" 87 | end 88 | 89 | def YankIsUnchanged 90 | yanks = GetYanks() 91 | yanks.length.should == 1 92 | yanks[0].should == "saved" 93 | end 94 | 95 | it "delete character" do 96 | @vim.type 'x' 97 | @vim.line.should == "irst" 98 | YankIsUnchanged() 99 | end 100 | 101 | it "delete word" do 102 | @vim.type 'dw' 103 | @vim.line.should == "" 104 | YankIsUnchanged() 105 | end 106 | 107 | it "delete line" do 108 | @vim.type 'dd' 109 | @vim.line.should == "second" 110 | YankIsUnchanged() 111 | end 112 | 113 | it "change word" do 114 | @vim.type 'cwnew' 115 | @vim.line.should == "new" 116 | YankIsUnchanged() 117 | end 118 | 119 | it "change line" do 120 | @vim.type 'ccnew' 121 | @vim.line.should == "new" 122 | YankIsUnchanged() 123 | end 124 | 125 | it "change select mode" do 126 | @vim.type 'venew' 127 | @vim.line.should == "new" 128 | YankIsUnchanged() 129 | end 130 | end 131 | 132 | ################### 133 | # Yanks 134 | ################### 135 | shared_examples "basic yanks" do 136 | 137 | before do 138 | AddExampleText() 139 | YankExampleText() 140 | end 141 | 142 | it "yanks are correct" do 143 | currentYanks = GetYanks() 144 | 145 | currentYanks[0].should == 'fourth' 146 | currentYanks[1].should == 'third' 147 | currentYanks[2].should == 'second' 148 | currentYanks[3].should == 'first' 149 | end 150 | 151 | it "yank rotation 1" do 152 | @vim.normal "p" 153 | @vim.normal "" 154 | 155 | currentYanks = GetYanks() 156 | 157 | currentYanks[0].should == 'third' 158 | currentYanks[1].should == 'second' 159 | currentYanks[2].should == 'first' 160 | currentYanks[-1].should == 'fourth' 161 | end 162 | 163 | it "yank rotation 2" do 164 | @vim.normal "p" 165 | 166 | @vim.normal "" 167 | @vim.line.should == "third" 168 | 169 | @vim.normal "" 170 | @vim.line.should == "second" 171 | 172 | @vim.normal "" 173 | @vim.line.should == "first" 174 | 175 | @vim.normal "" 176 | @vim.line.should == "second" 177 | 178 | @vim.normal "" 179 | @vim.line.should == "third" 180 | 181 | @vim.normal "" 182 | @vim.line.should == "fourth" 183 | 184 | @vim.normal "" 185 | @vim.line.should == "first" 186 | 187 | @vim.normal "" 188 | @vim.line.should == "second" 189 | 190 | # undo should undo the original paste completely 191 | @vim.undo 192 | @vim.line.should == "" 193 | end 194 | 195 | it "yank rotation 3" do 196 | 197 | @vim.normal "p" 198 | @vim.line.should == "fourth" 199 | @vim.undo 200 | 201 | @vim.type "[yp" 202 | @vim.line.should == "third" 203 | @vim.undo 204 | 205 | @vim.type "[yp" 206 | @vim.line.should == "second" 207 | @vim.undo 208 | 209 | @vim.type "]yp" 210 | @vim.line.should == "third" 211 | @vim.undo 212 | 213 | @vim.type "]yp" 214 | @vim.line.should == "fourth" 215 | @vim.undo 216 | end 217 | end 218 | 219 | ################### 220 | # PASTE 221 | ################### 222 | shared_examples "basic pasting" do 223 | 224 | before do 225 | AddExampleText() 226 | YankExampleText() 227 | @vim.normal "p" 228 | end 229 | 230 | it "pastes the most recently yanked string" do 231 | @vim.line_number.should == 5 232 | @vim.line.should == "fourth" 233 | end 234 | 235 | it "pastes in visual mode" do 236 | @vim.type "vip" 237 | @vim.type "p" 238 | @vim.line.should == "fourth" 239 | @vim.line_number.should == 1 240 | end 241 | 242 | it "pressing the repeat key with '.'" do 243 | @vim.type "." 244 | @vim.line.should == "fourthfourth" 245 | end 246 | 247 | it "pressing the repeat key with '.'" do 248 | @vim.type "." 249 | @vim.line.should == "fourthfourth" 250 | end 251 | 252 | it "pressing toggle after repeat" do 253 | @vim.type "." 254 | @vim.line.should == "fourthfourth" 255 | 256 | @vim.type "" 257 | @vim.line.should == "fourththird" 258 | 259 | @vim.type "" 260 | @vim.line.should == "fourthfourth" 261 | end 262 | end 263 | 264 | ################### 265 | # PASTE 2 266 | ################### 267 | shared_examples "basic paste auto formatting" do 268 | 269 | before do 270 | AddExampleText() 271 | end 272 | 273 | it "auto formatting" do 274 | @vim.insert " fifth" 275 | @vim.normal "0mm" 276 | @vim.line.should == "fourth" 277 | @vim.type "p" 278 | @vim.line.should == "fifth" 279 | end 280 | 281 | it "auto formatting 2" do 282 | @vim.insert " fifth" 283 | @vim.normal "0mm" 284 | @vim.insert " sixth" 285 | @vim.normal "p" 286 | @vim.line.should == " fifth" 287 | end 288 | end 289 | 290 | ################### 291 | # COMMAND MODE PASTE 292 | ################### 293 | shared_examples "command mode paste" do 294 | 295 | before do 296 | AddExampleYanks() 297 | end 298 | 299 | it "test 1" do 300 | # todo 301 | end 302 | end 303 | 304 | ################### 305 | # CUT OPERATOR 306 | ################### 307 | shared_examples "basic cutting/moving" do 308 | 309 | before do 310 | AddExampleText() 311 | end 312 | 313 | it "test cut word" do 314 | @vim.normal "gg" 315 | 316 | (1..4).each do |i| 317 | @vim.normal "mw" 318 | @vim.line.should == "" 319 | @vim.normal "j0" 320 | end 321 | 322 | currentYanks = GetYanks() 323 | 324 | currentYanks[0].should == "fourth" 325 | currentYanks[1].should == "third" 326 | currentYanks[2].should == "second" 327 | currentYanks[3].should == "first" 328 | end 329 | 330 | it "test cut line" do 331 | 332 | @vim.normal "gg" 333 | 334 | (1..4).each do |i| 335 | @vim.normal "mm" 336 | end 337 | 338 | GetNumLines().should == 1 339 | 340 | currentYanks = GetYanks() 341 | 342 | currentYanks[0].should == "fourth^M" 343 | currentYanks[1].should == "third^M" 344 | currentYanks[2].should == "second^M" 345 | currentYanks[3].should == "first^M" 346 | end 347 | end 348 | 349 | ################### 350 | # SUBSTITUTION OPERATOR 351 | ################### 352 | shared_examples "basic substitution" do 353 | 354 | before do 355 | AddExampleText() 356 | YankExampleText() 357 | end 358 | 359 | it "substitute word" do 360 | @vim.normal "gg" 361 | @vim.line.should == "first" 362 | @vim.normal "sw" 363 | @vim.line.should == "fourth" 364 | @vim.normal "j0" 365 | @vim.line.should == "second" 366 | @vim.type "." 367 | @vim.line.should == "fourth" 368 | end 369 | 370 | it "substitute line" do 371 | @vim.normal "gg" 372 | @vim.line.should == "first" 373 | @vim.normal "ss" 374 | @vim.line.should == "fourth" 375 | @vim.normal "j0" 376 | @vim.line.should == "second" 377 | @vim.type "." 378 | @vim.line.should == "fourth" 379 | end 380 | end 381 | 382 | shared_examples "test1" do 383 | 384 | before do 385 | AddExampleText() 386 | YankExampleText() 387 | end 388 | 389 | it "1" do 390 | yanks = GetYanks() 391 | 392 | yanks[0].should == "first" 393 | end 394 | end 395 | 396 | shared_examples "all tests" do 397 | 398 | it_has_behavior "basic substitution" 399 | it_has_behavior "basic cutting/moving" 400 | it_has_behavior "basic pasting" 401 | it_has_behavior "basic paste auto formatting" 402 | it_has_behavior "basic yanks" 403 | it_has_behavior "black hole redirection" 404 | end 405 | 406 | describe "clipboard default" do 407 | 408 | before do 409 | @vim.command("set clipboard=") 410 | end 411 | 412 | it_has_behavior "all tests" 413 | end 414 | 415 | describe "clipboard unnamed" do 416 | 417 | before do 418 | @vim.command("set clipboard=unnamed") 419 | end 420 | 421 | it_has_behavior "all tests" 422 | end 423 | 424 | describe "clipboard unnamed,unnamedplus" do 425 | 426 | before do 427 | @vim.command("set clipboard=unnamed,unnamedplus") 428 | end 429 | 430 | it_has_behavior "all tests" 431 | end 432 | 433 | ################### 434 | # Helper functions 435 | ################### 436 | def GetNumLines 437 | (@vim.command "echo line('$')").to_i 438 | end 439 | 440 | def GetYanks 441 | yanks = @vim.command("Yanks").split("\n")[1..-1] 442 | yanks = yanks.map { | y | y.match(/\d*\s*(.*)$/)[1] } 443 | 444 | return yanks 445 | end 446 | end 447 | -------------------------------------------------------------------------------- /spec/fixtures/repeat.vim: -------------------------------------------------------------------------------- 1 | " repeat.vim - Let the repeat command repeat plugin maps 2 | " Maintainer: Tim Pope 3 | " Version: 1.1 4 | " GetLatestVimScripts: 2136 1 :AutoInstall: repeat.vim 5 | 6 | " Installation: 7 | " Place in either ~/.vim/plugin/repeat.vim (to load at start up) or 8 | " ~/.vim/autoload/repeat.vim (to load automatically as needed). 9 | " 10 | " License: 11 | " Copyright (c) Tim Pope. Distributed under the same terms as Vim itself. 12 | " See :help license 13 | " 14 | " Developers: 15 | " Basic usage is as follows: 16 | " 17 | " silent! call repeat#set("\MappingToRepeatCommand",3) 18 | " 19 | " The first argument is the mapping that will be invoked when the |.| key is 20 | " pressed. Typically, it will be the same as the mapping the user invoked. 21 | " This sequence will be stuffed into the input queue literally. Thus you must 22 | " encode special keys by prefixing them with a backslash inside double quotes. 23 | " 24 | " The second argument is the default count. This is the number that will be 25 | " prefixed to the mapping if no explicit numeric argument was given. The 26 | " value of the v:count variable is usually correct and it will be used if the 27 | " second parameter is omitted. If your mapping doesn't accept a numeric 28 | " argument and you never want to receive one, pass a value of -1. 29 | " 30 | " Make sure to call the repeat#set function _after_ making changes to the 31 | " file. 32 | " 33 | " For mappings that use a register and want the same register used on 34 | " repetition, use: 35 | " 36 | " silent! call repeat#setreg("\MappingToRepeatCommand", v:register) 37 | " 38 | " This function can (and probably needs to be) called before making changes to 39 | " the file (as those typically clear v:register). Therefore, the call sequence 40 | " in your mapping will look like this: 41 | " 42 | " nnoremap MyMap 43 | " \ :execute 'silent! call repeat#setreg("\Plug>MyMap", v:register)' 44 | " \ call MyFunction(v:register, ...) 45 | " \ silent! call repeat#set("\Plug>MyMap") 46 | 47 | if exists("g:loaded_repeat") || &cp || v:version < 700 48 | finish 49 | endif 50 | let g:loaded_repeat = 1 51 | 52 | let g:repeat_tick = -1 53 | let g:repeat_reg = ['', ''] 54 | 55 | " Special function to avoid spurious repeats in a related, naturally repeating 56 | " mapping when your repeatable mapping doesn't increase b:changedtick. 57 | function! repeat#invalidate() 58 | let g:repeat_tick = -1 59 | endfunction 60 | 61 | function! repeat#set(sequence,...) 62 | let g:repeat_sequence = a:sequence 63 | let g:repeat_count = a:0 ? a:1 : v:count 64 | let g:repeat_tick = b:changedtick 65 | endfunction 66 | 67 | function! repeat#setreg(sequence,register) 68 | let g:repeat_reg = [a:sequence, a:register] 69 | endfunction 70 | 71 | function! repeat#run(count) 72 | if g:repeat_tick == b:changedtick 73 | let r = '' 74 | if g:repeat_reg[0] ==# g:repeat_sequence && !empty(g:repeat_reg[1]) 75 | if g:repeat_reg[1] ==# '=' 76 | " This causes a re-evaluation of the expression on repeat, which 77 | " is what we want. 78 | let r = '"=' . getreg('=', 1) . "\" 79 | else 80 | let r = '"' . g:repeat_reg[1] 81 | endif 82 | endif 83 | 84 | let c = g:repeat_count 85 | let s = g:repeat_sequence 86 | let cnt = c == -1 ? "" : (a:count ? a:count : (c ? c : '')) 87 | call feedkeys(r . cnt, 'n') 88 | call feedkeys(s) 89 | else 90 | call feedkeys((a:count ? a:count : '') . '.', 'n') 91 | endif 92 | endfunction 93 | 94 | function! repeat#wrap(command,count) 95 | let preserve = (g:repeat_tick == b:changedtick) 96 | exe 'norm! '.(a:count ? a:count : '').a:command . (&foldopen =~# 'undo' ? 'zv' : '') 97 | if preserve 98 | let g:repeat_tick = b:changedtick 99 | endif 100 | endfunction 101 | 102 | nnoremap . :call repeat#run(v:count) 103 | nnoremap u :call repeat#wrap('u',v:count) 104 | if maparg('U','n') ==# '' 105 | nnoremap U :call repeat#wrap('U',v:count) 106 | endif 107 | nnoremap :call repeat#wrap("\C-R>",v:count) 108 | 109 | augroup repeatPlugin 110 | autocmd! 111 | autocmd BufLeave,BufWritePre,BufReadPre * let g:repeat_tick = (g:repeat_tick == b:changedtick || g:repeat_tick == 0) ? 0 : -1 112 | autocmd BufEnter,BufWritePost * if g:repeat_tick == 0|let g:repeat_tick = b:changedtick|endif 113 | augroup END 114 | 115 | " vim:set ft=vim et sw=4 sts=4: 116 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | 2 | $LOAD_PATH.unshift("C:/Projects/Libraries/vimbot/lib") 3 | 4 | require "vimbot" 5 | 6 | PLUGIN_ROOT = File.expand_path("../..", __FILE__) 7 | VIM_REPEAT_PATH = File.expand_path("spec/fixtures/repeat.vim", PLUGIN_ROOT) 8 | 9 | RSpec.configure do |c| 10 | c.alias_it_should_behave_like_to :it_has_behavior, 'has behavior:' 11 | end 12 | 13 | -------------------------------------------------------------------------------- /tests/Test1.py: -------------------------------------------------------------------------------- 1 | 2 | import re 3 | import os 4 | import time 5 | import sys 6 | import unittest 7 | from ave.util.Log import Log 8 | from vimdriver.VimDriver import VimDriver 9 | from ave.util import FileUtil 10 | 11 | Log.setMinLevel(Log.Levels.info) 12 | #Log.setMinLevel(Log.Levels.debug) 13 | 14 | # Enabling this keeps it open and makes it easier to debug 15 | UseExistingVim = True 16 | 17 | ScriptDir = os.path.dirname(os.path.realpath(__file__)) 18 | 19 | class Tests1(unittest.TestCase): 20 | 21 | def setUp(self): 22 | self.driver = VimDriver() 23 | 24 | if UseExistingVim and self.driver.isServerUp: 25 | self.driver.clearBuffer() 26 | else: 27 | vimrc = FileUtil.ChangeToForwardSlashes(os.path.join(ScriptDir, 'TestVimRc.vim')) 28 | self.driver.start(vimrc) 29 | 30 | def tearDown(self): 31 | if not UseExistingVim: 32 | self.driver.stop() 33 | 34 | def testRepeatVimLoaded(self): 35 | # Will throw exception otherwise 36 | self.driver.command('call repeat#invalidate()') 37 | 38 | def testEasyClipLoaded(self): 39 | # Will throw exception otherwise 40 | defaultReg = self.driver.evaluate('EasyClip#GetDefaultReg()') 41 | self.assertEqual(defaultReg, '"') 42 | 43 | if __name__ == '__main__': 44 | unittest.main() 45 | -------------------------------------------------------------------------------- /tests/TestVimRc.vim: -------------------------------------------------------------------------------- 1 | " Mostly taken from sensible.vim 2 | 3 | set nocompatible 4 | 5 | set visualbell 6 | set noerrorbells 7 | 8 | if has('autocmd') 9 | filetype plugin indent on 10 | endif 11 | 12 | if has('syntax') && !exists('g:syntax_on') 13 | syntax enable 14 | endif 15 | 16 | " Use :help 'option' to see the documentation for the given option. 17 | 18 | set autoindent 19 | set backspace=indent,eol,start 20 | set complete-=i 21 | set smarttab 22 | 23 | set nrformats-=octal 24 | 25 | set ttimeout 26 | set ttimeoutlen=100 27 | 28 | set incsearch 29 | " Use to clear the highlighting of :set hlsearch. 30 | if maparg('', 'n') ==# '' 31 | nnoremap :nohlsearch 32 | endif 33 | 34 | set laststatus=2 35 | set ruler 36 | set showcmd 37 | set wildmenu 38 | 39 | if !&scrolloff 40 | set scrolloff=1 41 | endif 42 | if !&sidescrolloff 43 | set sidescrolloff=5 44 | endif 45 | set display+=lastline 46 | 47 | if &encoding ==# 'latin1' && has('gui_running') 48 | set encoding=utf-8 49 | endif 50 | 51 | if &listchars ==# 'eol:$' 52 | set listchars=tab:>\ ,trail:-,extends:>,precedes:<,nbsp:+ 53 | endif 54 | 55 | if has('path_extra') 56 | setglobal tags-=./tags tags^=./tags; 57 | endif 58 | 59 | if &shell =~# 'fish$' 60 | set shell=/bin/bash 61 | endif 62 | 63 | set autoread 64 | set fileformats+=mac 65 | 66 | if &history < 1000 67 | set history=1000 68 | endif 69 | if &tabpagemax < 50 70 | set tabpagemax=50 71 | endif 72 | if !empty(&viminfo) 73 | set viminfo^=! 74 | endif 75 | set sessionoptions-=options 76 | 77 | " Allow color schemes to do bright colors without forcing bold. 78 | if &t_Co == 8 && $TERM !~# '^linux' 79 | set t_Co=16 80 | endif 81 | 82 | " Load matchit.vim, but only if the user hasn't installed a newer version. 83 | if !exists('g:loaded_matchit') && findfile('plugin/matchit.vim', &rtp) ==# '' 84 | runtime! macros/matchit.vim 85 | endif 86 | 87 | inoremap u 88 | 89 | let s:fileDir=expand(":p:h") 90 | 91 | " Add repeat.vim, etc. 92 | exec "set rtp+=" . s:fileDir . "/vimfiles" 93 | exec "set rtp+=" . s:fileDir . "/.." 94 | 95 | -------------------------------------------------------------------------------- /tests/vimfiles/plugin/repeat.vim: -------------------------------------------------------------------------------- 1 | " repeat.vim - Let the repeat command repeat plugin maps 2 | " Maintainer: Tim Pope 3 | " Version: 1.1 4 | " GetLatestVimScripts: 2136 1 :AutoInstall: repeat.vim 5 | 6 | " Installation: 7 | " Place in either ~/.vim/plugin/repeat.vim (to load at start up) or 8 | " ~/.vim/autoload/repeat.vim (to load automatically as needed). 9 | " 10 | " License: 11 | " Copyright (c) Tim Pope. Distributed under the same terms as Vim itself. 12 | " See :help license 13 | " 14 | " Developers: 15 | " Basic usage is as follows: 16 | " 17 | " silent! call repeat#set("\MappingToRepeatCommand",3) 18 | " 19 | " The first argument is the mapping that will be invoked when the |.| key is 20 | " pressed. Typically, it will be the same as the mapping the user invoked. 21 | " This sequence will be stuffed into the input queue literally. Thus you must 22 | " encode special keys by prefixing them with a backslash inside double quotes. 23 | " 24 | " The second argument is the default count. This is the number that will be 25 | " prefixed to the mapping if no explicit numeric argument was given. The 26 | " value of the v:count variable is usually correct and it will be used if the 27 | " second parameter is omitted. If your mapping doesn't accept a numeric 28 | " argument and you never want to receive one, pass a value of -1. 29 | " 30 | " Make sure to call the repeat#set function _after_ making changes to the 31 | " file. 32 | " 33 | " For mappings that use a register and want the same register used on 34 | " repetition, use: 35 | " 36 | " silent! call repeat#setreg("\MappingToRepeatCommand", v:register) 37 | " 38 | " This function can (and probably needs to be) called before making changes to 39 | " the file (as those typically clear v:register). Therefore, the call sequence 40 | " in your mapping will look like this: 41 | " 42 | " nnoremap MyMap 43 | " \ :execute 'silent! call repeat#setreg("\Plug>MyMap", v:register)' 44 | " \ call MyFunction(v:register, ...) 45 | " \ silent! call repeat#set("\Plug>MyMap") 46 | 47 | if exists("g:loaded_repeat") || &cp || v:version < 700 48 | finish 49 | endif 50 | let g:loaded_repeat = 1 51 | 52 | let g:repeat_tick = -1 53 | let g:repeat_reg = ['', ''] 54 | 55 | " Special function to avoid spurious repeats in a related, naturally repeating 56 | " mapping when your repeatable mapping doesn't increase b:changedtick. 57 | function! repeat#invalidate() 58 | let g:repeat_tick = -1 59 | endfunction 60 | 61 | function! repeat#set(sequence,...) 62 | let g:repeat_sequence = a:sequence 63 | let g:repeat_count = a:0 ? a:1 : v:count 64 | let g:repeat_tick = b:changedtick 65 | endfunction 66 | 67 | function! repeat#setreg(sequence,register) 68 | let g:repeat_reg = [a:sequence, a:register] 69 | endfunction 70 | 71 | function! repeat#run(count) 72 | if g:repeat_tick == b:changedtick 73 | let r = '' 74 | if g:repeat_reg[0] ==# g:repeat_sequence && !empty(g:repeat_reg[1]) 75 | if g:repeat_reg[1] ==# '=' 76 | " This causes a re-evaluation of the expression on repeat, which 77 | " is what we want. 78 | let r = '"=' . getreg('=', 1) . "\" 79 | else 80 | let r = '"' . g:repeat_reg[1] 81 | endif 82 | endif 83 | 84 | let c = g:repeat_count 85 | let s = g:repeat_sequence 86 | let cnt = c == -1 ? "" : (a:count ? a:count : (c ? c : '')) 87 | call feedkeys(r . cnt, 'n') 88 | call feedkeys(s) 89 | else 90 | call feedkeys((a:count ? a:count : '') . '.', 'n') 91 | endif 92 | endfunction 93 | 94 | function! repeat#wrap(command,count) 95 | let preserve = (g:repeat_tick == b:changedtick) 96 | exe 'norm! '.(a:count ? a:count : '').a:command . (&foldopen =~# 'undo' ? 'zv' : '') 97 | if preserve 98 | let g:repeat_tick = b:changedtick 99 | endif 100 | endfunction 101 | 102 | nnoremap . :call repeat#run(v:count) 103 | nnoremap u :call repeat#wrap('u',v:count) 104 | if maparg('U','n') ==# '' 105 | nnoremap U :call repeat#wrap('U',v:count) 106 | endif 107 | nnoremap :call repeat#wrap("\C-R>",v:count) 108 | 109 | augroup repeatPlugin 110 | autocmd! 111 | autocmd BufLeave,BufWritePre,BufReadPre * let g:repeat_tick = (g:repeat_tick == b:changedtick || g:repeat_tick == 0) ? 0 : -1 112 | autocmd BufEnter,BufWritePost * if g:repeat_tick == 0|let g:repeat_tick = b:changedtick|endif 113 | augroup END 114 | 115 | " vim:set ft=vim et sw=4 sts=4: 116 | --------------------------------------------------------------------------------