├── .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