conditional map in normal mode? - vim

Is it possible in vim to do a conditional map, in normal mode?
I've seen it for insert mode.
I want to remap gq, depending on the outcome of a function.
Something like:
nnoremap gq if(g:set_formatprg()) | gq | else | = | endif
Notice that g:set_formatprg() will not always have the same value, so it cannot be replaced by
if(!g:set_formatprg()) | nnoremap gq = | endif

An expression map makes it easy
nnoremap <expr> gq g:set_formatprg() ? 'gq' : '='
For more help see
:h map-expression

Related

How to make f and t wrap around the current line in vim

Is there a way to make the 'f' and 't' command wrap around the line? For example, if I have
Hello, my name is _intz,
where _ denotes my cursor position, I would like to be able to press fl for vim to place my cursor on the first l on the line.
Similarly, I would ideally like the , and ; commands to also wrap on the current line.
Thank you
No, this is not possible without implementing the feature yourself.
Note that fF are universally expected to mean "next on the line" and tT to mean "previous on the line", both of which being extremely useful in their own right. Instead of changing their meaning, and thus reducing the overall usefulness of Vim, you should consider making new commands.
Something like these quick and dirty mappings:
" move the cursor on first occurrence of character on the line
nnoremap <expr> <key> '0f' . nr2char(getchar())
" move the cursor before first occurrence of character on the line
nnoremap <expr> <key> '0t' . nr2char(getchar())
See :help <expr>, :help nr2char(), :help getchar().
With the help of this answer https://vi.stackexchange.com/questions/29167/determine-if-there-is-a-matching-character-on-the-current-line-past-the-cursor, the following maps <c-f> to allow that gives it the functionality of f with same line wrapping.
function!Neweff()
let character = nr2char(getchar())
let beforejump = getpos('.')
execute 'norm! f'.character.''
let afterjump = getpos('.')
if beforejump == afterjump
let firstcharacter = getline(".")[0]
execute 'norm! 0'
if character !=# firstcharacter
execute 'norm! f'.character.''
endif
endif
endfunction
nnoremap <c-f> :call Neweff()<CR>

How to shortcut (nmap) windo command expression?

For closing all quickfix windows, I use the following vim command expression:
:windo if &buftype == 'quickfix' || &buftype == 'locationlist' | lclose | endif
Whenever I try to shortcut it like:
nmap <S-q> :let #a = "%:windo if &buftype == 'quickfix' || &buftype == 'locationlist' | lclose | endif"
by using nmap in my init.vim, I got an error:
E749: empty buffer
E488: Trailing characters
How to solve that?
Explanation
The | is a command separator; unfortunately (this is a common pitfall), it also ends any :map command, and the remainder is interpreted immediately, instead of being part of the mapping.
:help map-bar lists three different solutions; the most common is using the special <Bar> notation instead of |.
Your mapping
nmap <S-q> :let #a = "%:windo if &buftype == 'quickfix' <Bar><Bar> &buftype == 'locationlist' <Bar> lclose <Bar> endif"
Is the %:windo a typo? The % is suspicious.
The mapping is missing the trailing <CR>; it will linger until you press <Enter> yourself. Is that intended?
Why do you assign the commands to register a instead of executing it?
You should use :noremap; it makes the mapping immune to remapping and recursion.
'buftype' is always quickfix, also for location lists; you can drop the second branch in the test.
noremap <S-q> :windo if &buftype == 'quickfix' <Bar> lclose <Bar> endif<CR>

vim: alias yank and copy to xclip

I've got a cntrl c and cntrl v mapped to xclip, however its a hassle to have to remember to use instead of regular y and p. Is there a way to alias the two or send contents of y and p to xclip, so I can just use y and p for all copy and pasting?
vmap <C-c> y:call system("xclip -i -selection clipboard", getreg("\""))<CR>:call system("xclip -i", getreg("\""))<CR>
nmap <C-v> :call setreg("\"",system("xclip -o -selection clipboard"))<CR>p")")")"))
Are you trying to use the X clipboard for all copy and pastes? If so, a good alternative to xclip is to make sure you're using a vim with X support (it's really easy to compile Vim if your version doesn't have it) and then add the following to your vimrc:
set clipboard=unnamed
All yanks and deletes will then automatically go to the * register (which is the X selection register).
Instead of setting clipboard=unnamed, you can also use the X selection register for a single operation by using (e.g.)
"*yw
"*yy
"*ya(
or whatever.
Obviously, this doesn't answer your question as to how to use xclip, but hopefully it offers an alternative approach.
I don't know if this is an ideal solution, but it works.
set clipboard=unnamedplus
function! ClipboardYank()
call system('xclip -i -selection clipboard', ##)
endfunction
let vlcb = 0
let vlce = 0
function! ClipboardPaste(mode)
if (a:mode == "v")
call cursor(g:vlcb[0], g:vlcb[1]) | execute "normal! v" | call cursor(g:vlce[0], g:vlce[1])
endif
let ## = system('xclip -o -selection clipboard')
endfunction
" replace currently selected text with default register without yanking it
vnoremap <silent>p "_dP
vnoremap <silent>y y:call ClipboardYank()<CR>
vnoremap <silent>d d:call ClipboardYank()<CR>
nnoremap <silent>dd dd:call ClipboardYank()<CR>
nnoremap <silent>p :call ClipboardPaste("n")<CR>p
vnoremap p :<C-U>let vlcb = getpos("'<")[1:2] \| let vlce = getpos("'>")[1:2] \| call ClipboardPaste("v")<CR>p
You can also set clipboard=unnamedplus to use the “+” register by default. Using it, I did yy in my .vimrc and then pasted that line here by ctrl-V ;)

vim function for toggling colorschemes

At the moment I'm using two different keys to toogle the colorscheme
map <F8> :colors wombat256 <cr>
map <F9> :colors dimtag <cr>
I want to achieve a toggle behavior like this
function! ToggleDimTags()
if (g:colors_name == "wombat256")
colors dimtag
else
colors wombat256
endif
endfunction
My problem is that ToogleDimTags() is resetting the cursor position to the first line on every call, which is undesirable. Any suggestions appreciated.
As discussed in the comments, the problem is that your map calling :execute
behaves a little differently, what you probably want is :call instead:
nnoremap <F5> :call ToggleDimTags()
To clarify what #ZyX was saying, :h :exec contains the following text:
:exe :execute
:exe[cute] {expr1} .. Executes the string that results from the evaluation
of {expr1} as an Ex command.
[...]
So what :execute really does is evaluating the expression looking for a string
that will be executed as an Ex command (also called colon commands). In other words:
exec ToggleDimTags() | " <-- ToggleDimTags() is evaluated and returns 0
exec 0
Which is:
:0
Now, :h :call:
:cal :call E107 E117
:[range]cal[l] {name}([arguments])
Call a function. The name of the function and its arguments
are as specified with |:function|. Up to 20 arguments can be
used. **The returned value is discarded**.
[...]
Update
I've been thinking about your function, and using the ternary operator and a bit
of :execute magic, you can simplify it up to a point where you discard the extra
function:
nnoremap <silent> <F9> :exec "color " .
\ ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")<CR>
Here, this nnoremap will not produce output (<silent>) and is based on
:exec, which is followed by this expression:
"color " . ((g:colors_name == "wombat256") ? "dimtag" : "wombat256")
When g:colors_name is set to wombat256, the expression evaluates to:
"color dimtag"
Or, otherwise:
"color wombat256"
Then either one is evaluated by :exec. Of course you can join the lines
(without forgetting to remove the backslash), I did it like this simply to avoid
a too long line.

How to paste over without overwriting register

Does anyone know of a way to paste over a visually selected area without having the selection placed in the default register?
I know I can solve the problem by always pasting from an explicit register. But it's a pain in the neck to type "xp instead of just p
Use the following:
xnoremap p pgvy
this will reselect and re-yank any text that is pasted in visual mode.
Edit: in order this to work with "xp you can do:
xnoremap p pgv"#=v:register.'y'<cr>
v:register expands to the last register name used in a normal mode command.
I don't like the default vim behavior of copying all text deleted with d, D, c, or C into the default register.
I've gotten around it by mapping d to "_d, c to "_c, and so on.
From my .vimrc:
"These are to cancel the default behavior of d, D, c, C
" to put the text they delete in the default register.
" Note that this means e.g. "ad won't copy the text into
" register a anymore. You have to explicitly yank it.
nnoremap d "_d
vnoremap d "_d
nnoremap D "_D
vnoremap D "_D
nnoremap c "_c
vnoremap c "_c
nnoremap C "_C
vnoremap C "_C
"{register}p won't work as you describe. It will replace the selection with the content of the register. You will have instead to do something like:
" I haven't found how to hide this function (yet)
function! RestoreRegister()
let #" = s:restore_reg
return ''
endfunction
function! s:Repl()
let s:restore_reg = #"
return "p#=RestoreRegister()\<cr>"
endfunction
" NB: this supports "rp that replaces the selection by the contents of #r
vnoremap <silent> <expr> p <sid>Repl()
Which should be fine as long as you don't use a plugin that has a non-nore vmap to p, and that expects a register to be overwritten.
This code is available as a script there. Ingo Karkat also defined a plugin solving the same issue.
In your .vimrc
xnoremap p "_dP
I found this from a response on a similar thread, but the original source was http://vim.wikia.com/wiki/Replace_a_word_with_yanked_text. It mentions some drawbacks, however it works fine for me.
Luc Hermitte's solution works like a charm. I was using it for about a week or so. Then I discovered a solution from Steve Losh's .vimrc that works nicely if YankRing is part of your plugin/bundle lineup:
function! YRRunAfterMaps()
" From Steve Losh, Preserve the yank post selection/put.
vnoremap p :<c-u>YRPaste 'p', 'v'<cr>gv:YRYankRange 'v'<cr>
endfunction
Try this in your ~/.vimrc:
xnoremap <expr> p 'pgv"'.v:register.'y'
xnoremap means that this is only for Visual mode, not Visual + Select modes.
<expr> means that {rhs} of the xnoremap {lhs} {rhs} setting is evaluated as an expression.
In this case, our expression of 'pgv"'.v:register.'y' is using . for concatenation.
v:register is evaluated to the register being used during the fulfillment of the mapping.
The result of "xp would evaluate to pgv"xy, where x is the register.
I was helped by an answer to this stackoverflow question: Vim - mapping with an optional register prefix
in conjunction with Benoit's answer on this page
Luc's function worked well for me after I made a change to support the fact that I have clipboard=unnamed set:
function! RestoreRegister()
let #" = s:restore_reg
if &clipboard == "unnamed"
let #* = s:restore_reg
endif
return ''
endfunction
Use P to paste without yanking the deleted text.
:help v_P
With P the unnamed register is not changed (and neither the selection or clipboard), you can repeat the same change.
This behavior was introduced in v8.2.4242 (2022-01-28) and refined in v8.2.4881 (2022-05-06).
Or if your muscle memory is too strong:
xnoremap p P
Luc Hermitte's did the trick! Really good. Here's his solution put in a toggle function, so you can switch between normal behavior and no-replace-register put.
the command ,u toggles the behavior
let s:putSwap = 1
function TogglePutSwap()
if s:putSwap
vnoremap <silent> <expr> p <sid>Repl()
let s:putSwap = 0
echo 'noreplace put'
else
vnoremap <silent> <expr> p p
let s:putSwap = 1
echo 'replace put'
endif
return
endfunction
noremap ,p :call TogglePutSwap()<cr>
This is my solution.
vnoremap p p:let #+=#0<CR>
vnoremap P P:let #+=#0<CR>
I find out after paste, the old content is still stored in "0 register.
Just restore it to current clipboard by
:let #+=#0
duct-tape programming, but works for me:
nmap viwp viwpyiw
nmap vi'p vi'pyi'
nmap vi"p vi"pyi"
nmap vi(p vi(pyi(
nmap vi[p vi[pyi[
nmap vi<p vi<pyi<
Select the text and paste by P(uppercase).
Example:
viwP
See h: v_P for more infomation.
try -
:set guioptions-=a
:set guioptions-=A

Resources