How to automatically remove newline at end of yanked linewise visual selection? - vim

Is there any way to yank a linewise visual selection without the newline at the end of the last line of the selection? More specifically, I'd like to be able to copy a line or lines from vim to the system clipboard and paste it elsewhere without the last command being executed without a chance to edit it.
I can get the desired effect by executing this command on the register in question after yanking:
:let #*=substitute(#*,'\n$','','g')
but is there any way to execute that command automatically? I am using MacVim at the moment and there doesn't appear to be a way to map an extra command to ⌘-C, so I'd have to have another mapping to remember to execute after copying if I can't find another solution.

My original answer to myself has been working well enough that I pretty much forgot about it, but now I've decided to set clipboard=unnamed to always yank to the system clipboard. A side effect is that yanking to the clipboard also affects the unnamed register, from which I don't want the trailing newline to be removed.
The new (and more Vim-ish) answer is a mapping to convert the linewise visual selection to characterwise before yanking it:
vnoremap <C-c> <Esc>'<0v'>g_y
Now I copy with <C-c> if I intend to paste in a command line or just y any other time. Pretty much what user Explosion Pills suggested but some added stuff to include the first character of the first line and last of the last line.

I came up with a solution that works for me, inspired by one of the answers here: Run command when vim enters visual mode
There is no real equivalent to a hypothetical VisualEnter or VisualLeave, but the CursorHold event along with changing updatetime will trigger after exiting visual mode. I also added the gv map to make it work when copying the previous selection.
" Remove last newline after copying visual selection to clipboard
function! RemoveClipboardNewline()
if &updatetime==1
let #*=substitute(#*,'\n$','','g')
set updatetime=4000
endif
endfunction
function! VisualEnter()
set updatetime=1
endfunction
vnoremap <expr> <SID>VisualEnter VisualEnter()
nnoremap <script> V V<SID>VisualEnter
nnoremap <script> gv gv<SID>VisualEnter
autocmd CursorHold * call RemoveClipboardNewline()
I don't like the idea of writing a swap file every 0 milliseconds, but I tested this in both Windows and OS X, and it works and causes no noticeable performance problems. It doesn't appear to write the swap file unless there are changes.
Also, I'm a little confused as to why this works at all, since it should trigger the function and set updatetime back to 4000 while still in visual mode, but apparently the value of updatetime doesn't change until after exiting visual mode, which works out nicely here.

Related

Make sure all indents in a block of text are the same in vim

I am having the hardest time indenting things consistently in vim, and get no end of IndentationError in python. Even setting all these tab-related settings: https://stackoverflow.com/a/234578/651174 makes things no better.
How would I do the following in vim to normalize all indentation for a block of text? https://gyazo.com/a333f05e8b4d8034967029005f77ea27
I've tried doing the ctrl v visual mode, but can't seem to figure out how to use that for my purpose, or if there's something else I should be doing here. What would be the best way to do the above?
use visual mode to select a block of text and hit '=' to indent the code correctly. To effect the whole file use gg=G, which translates to go to the beginning of the file and indent correctly to the bottom of the file.
Put the following in your vimrc and use :ReformatFile for easy re-indenting.
function ReformatFile()
exec "normal! mqHmw"
exec "normal! gg=G"
exec "normal! 'wzt`q"
endfunction
command ReformatFile silent call ReformatFile()
mqHmw marks your current position and and the top of your current window see :h marks
"'wzt`q" moves your cursor to the mark w, moves the line to the top of the file and than to the line and column of mark q
Sry for my bad english, but try to type out the commands in vim and read the docs. The marks q/w are chosen arbitrary.

vimscript: How to substitute in current selection?

Inside a function I can use:
s/foo/bar/ge
but it only substitutes the current line.
I'd like to substitute in the current selection. I tried
'<,'>s/foo/bar/ge
with no success.
Any help is appreciated.
This is perfectly fine:
fun! Foo()
'<,'>s/foo/bar/ge
endfun
You may get E20: Mark not set when no visual selection has yet been established, though. For the '<,'> marks to be defined, visual mode must have been left already; but this is also accomplished by the : command that is used to invoke the function, so it shouldn't be a problem (except for special cases like :help :map-<expr>). If you establish the visual selection only within the function, you need to leave it. Instead of
:normal! Vjj
append a <Esc> to leave visual mode (and set the marks):
:execute normal! "Vjj\<Esc>"
Note that hard-coding the selection often is bad style; you usually want a mapping to work either on the selection, or [count] lines, or the current line / entire buffer. For that, it's advisable to define the function with the range attribute; see :help function-range-example for details.

Vim-commands pasted from clipboard inserts commands instead of executing them

For quick execution of some commands I want to type them somewhere, then paste them into vim for execution. A while (some years or 10 years ago) this worked. Nowadays, vim enters insert mode and my commands get inserted into my file. I know that I can have macros for that, but for quick repetition of some commands this was very useful. Couldnt find anything about it, coz when asking for vim and paste only answers related to :set paste etc show up. Example of pasted text:
/foo
yy?bar
p/foo
j
Should, when pasted in command mode, search for the next foo, yank the line, search backwards for next bar, then paste the yanked foo-line, then go behind that foo-line.
Again, to make it clear: I dont want to know about workarounds (makros, scripts, and so on), I want to know how to disable (temporarily) vim from distinguishing between text typed with keyboard and text pasted.
This is the result of "bracketed paste mode". See :help xterm-bracketed-paste.
Disable "bracketed paste mode" by clearing 't_BE':
set t_BE=
If your Vim has been compiled with clipboard support then you can just leave 't_BE' as is and instead execute clipboard as a register via #* or #+ which will probably be more "idiomatic" way to handle such use case.
For more informations see :h #.
I had this same problem...
I have LARGE files of vim commands that I use to bulk reformatting hundreds of plain text files. That is I would: paste commands, next file, paste commands, next file, and in so doing edit hundreds of files, with the given set of vim commands, in just a minute or so...
Then it all suddenly stopped working because Bracketed Paste mode was added... (part of patch 8.0.0238 I believe).
However I still liked the added ability to paste text when in insert mode, without vim auto-indent making a mess of the inserted text, so did not want to lose that either...
My solution, was to disable paste while in normal or command modes,
while leaving brackets paste as in for insert mode...
nmap <PasteStart> <NOP>
nmap <PasteEnd> <NOP>
cmap <PasteStart> <NOP>
cmap <PasteEnd> <NOP>

gvim auto copy selected text to system clipboard (to share it with apps )

I wonder, how i can enable auto copy of selected text into '+'
register in Ubuntu (to share clipboard between apps)?
On win XP, i have
set guioptions+=a
and its works perfectly, but not in Ubuntu 11.10.
Also, i tried
set clipboard=unnamedplus,unnamed,autoselect,exclude:cons\|linux.
but without success.
Please do not offer hand-click solutions like
vmap <C-Insert> "+y and mouse copy/paste.
test case (with "behave mswin" option):
open gvim
shift-v, move cursor and Esc (select lines in visual mode)
go to firefox and click ctrl-v or ctrl-Insert to paste text
Solution
In this thread, problem was solved.
You need to apply patch from Christian Brabandt.
Also, if you have problem with paste with shift-insert after recompilation in ubuntu, you can add this in your vimrc:
if has("gui_running")
map <silent> <S-Insert> "+p
cmap <S-Insert> <C-R>+
imap <silent> <S-Insert> <Esc>"+pa
endif
Does "+y work? It's not a suggestion: if this command doesn't work, you may have some underlying problems that prevent a simple solution. So it needs to be checked first, even if it sounds stupid.
set clipboard+=unnamedplus is enough if your version of Vim supports it. Mine is 7.3.35 and it doesn't work (Vim doesn't complain, though).
I don't know exactly which patch introduced unnamedplus but you can do :help 'clipboard' (with the single quotes) to have a list of available options. If unnamedplus is listed, the snippet above should fix your problem. If it's not there you won't be able to use it (obviously): time to re-assess your "do not offer hand-click solutions like vmap "+y and mouse copy/paste" requirement or compile a more recent version of Vim.
Try following:
set guioptions+=P
Explanation:
TLDR: a puts text into "* register. P puts text into "+ register
From :help guioptions
'a' Autoselect: If present, then whenever VISUAL mode is started,
or the Visual area extended, Vim tries to become the owner of the
windowing system's global selection. This means that the Visually
highlighted text is available for pasting into other applications as
well as into Vim itself. When the Visual mode ends, possibly due to
an operation on the text, or when an application wants to paste the
selection, the highlighted text is automatically yanked into the "*
selection register. Thus the selection is still available for
pasting into other applications after the VISUAL mode has ended.
If not present, then Vim won't become the owner of the windowing system's global selection unless explicitly told to by a
yank or delete operation for the "* register. The same applies to
the modeless selection.
'P' Like autoselect but using the "+ register instead of the "*
register.

Pasting from the clipboard and automatically toggling ":set paste"

When I paste things from the clipboard, they're normally (always) multilined, and in those cases (and those cases only), I'd like :set paste to be triggered, since otherwise the tabbing will increase with each line (you've all seen it!).
Though the problem with :set paste is that it doesn't behave well with set smartindent, causing the cursor to jump to the beginning of a new line instead of at the correct indent. So I'd like to enable it for this instance only.
I'm using Mac, sshing to a Debian machine with Vim, and thus pasting in Insert mode using cmd + v.
I don't use a mac, but I believe I have the prefix right here: <D-v> should mean cmd-v. For insert mode:
:imap <D-v> ^O:set paste<Enter>^R+^O:set nopaste<Enter>
or really, just do this:
:imap <D-V> ^O"+p
The ^O and ^R are literal control-O and control-R, which you can type with ^V^O (control-v control-o) and ^V^R (control-v control-r). Control-O in insert mode allows you to execute one command then return to insert mode; here you can use it to put from the clipboard register.
This worked for me when I tested them mapped to a different key, so you should be all set.
There's no need to map anything when not in insert mode; you can just use "+p.
I have the following in my .vimrc:
inoremap <S-Insert> <ESC>:setl paste<CR>gi<C-R>+<ESC>:setl nopaste<CR>gi
gi is to start insert mode in the same position as where insert mode was stopped last time in the current buffer.
Update:
Jefromi posted a better solution. I have tinkered it a bit
inoremap <S-Insert> <ESC>"+p`]a
It inserts clipboard text and places the cursor right after it.
You're right in that you should only enable 'paste' when you need it. It does more than just affect indenting. You can read everything that it affects in its documentation. A related option that is very useful to ease the use of 'paste' is 'pastetoggle'.
If you were using X-forwarding and a terminal that can properly communicate mouse actions, you could also take advantage of the 'mouse' option. With :set mouse=a, Vim is made aware of what the mouse is doing and therefore won't perform automatic indentation when it receives a multi-line paste via a middle-button mouse click.
Even without the mouse capability, X-forwarding could help because Vim will do the same thing when manually pasting from the clipboard or selection registers ("+ and "* respectively).
This ought to be solvable with a Vim script. (I hate Vim scripting, so it would have to be a much more serious infliction to cause me to solve it myself.) Even with iTerm2's "paste slowly" mode, the default is to break the data to be pasted into 16 byte chunks and send one every 0.125 seconds. Therefore, you should be able to programmatically detect a 16 byte chunk of "keystrokes" and do something about it.
In pseudocode that would look like:
if too_fast_too_be_human():
set('pastemode', True)
else
set('pastemode', False)
# where either
def too_fast_too_be_human
char_threshold = 16
return len(input_buffer) > char_threshold
# or
def too_fast_too_be_human
static byte_times = []
char_threshold = 16
time_threshold = 0.125
byte_times.append(now())
while(len(byte_times) > char_threshold):
byte_times.unshift()
return (byte_times[-1] - byte_times[0]) < time_threshold
There are weaknesses to that, but it would work for most cases.
We can paste (insert mode) without messing up indentation using
Ctrl-r Ctrl-o Register
Ctrl-r Ctrl-o +
Ctrl-r Ctrl-o *
Ctrl-r Ctrl-o 0
CTRL-R CTRL-O {0-9a-z"%#*+/:.-=} *i_CTRL-R_CTRL-O*
Insert the contents of a register literally and don't
auto-indent. Does the same as pasting with the mouse
"MiddleMouse". When the register is linewise this will
insert the text above the current line, like with `P`.
Does not replace characters!
The '.' register (last inserted text) is still inserted as
typed.

Resources