How to call vim function from within map and ":split" - vim

I would like to map a key using "map" from within my vimrc file like this:
map <C-I> :split ~/some/file
That command actually works fine.
My question is: how do I call a vim function (in this case, "resolve()") on that file path from within the map/split line. This doesn't work, but hopefully you get the point:
map <C-I> :split =resolve("~/some/file")
Perhaps it uses call()? I'm obviously confused about vim scripting in general. Thanks for your help!

There are two additional ways of doing this which will work outside of a mapping and are safer then using <C-r> (POSIX allows filenames with any byte but \x00, including control codes):
nnoremap <C-i> :execute "split" fnameescape(resolve("~/some/file"))<CR>
nnoremap <C-i> :split `=resolve("~/some/file")`<CR>
In second case no escaping is needed, but filename must not contain newline (it won't hurt, just will produce an error).
Another things to consider:
Use nnoremap, it will enable you to, for example, exchange meanings of ; and : without changing maps and also prevent your map from being spoiled by plugins unless they redefine <Tab> mapping (<C-i> is same as <Tab>). Forced normal mode is here because in other modes it will produce unexpected results.
Escape arguments: fnameescape(resolve("~/some/file")), it will prevent errors for filenames with spaces.
You can write <C-r> where #Austin Taylor suggested to write raw control code. I don't like having any of them inside a file because it will make diffs not viewable in a terminal.

map <C-I> :split ^R=resolve("~/some/file")<cr><cr>
If you are putting this in .vimrc, you type C-v C-r to type the ^R character.

Related

Some key mappings for combinations with the PageUp/PageDown keys don't work

I use rxvt-unicode terminal emulator on Manjaro, and the following two mappings in my .vimrc don't work,
nnoremap <C-PageUp> :tabprevious<CR>
nnoremap <C-PageDown> :tabnext<CR>
even though the following does work
nnoremap <C-t> :tabnew<CR>
I think the reason for the behavior you observe is exactly the one described here.
In other words, something like this (what the rhs is doesn't matter)
nnoremap <C-PageUp> :echo "hello"<CR>
will not work as Vim does not now which escape sequence corresponds to the keycode <C-PageUp>.
Therefore, you can provide it with the escape sequence corresponding to Ctrl-PageUp, as in
nnoremap ^[[5^ :echo "hello"<CR>
where the first two characters of the escape sequence, ^[, are part of a single unit that corresponds to Escape (that's why escape seqeunce).
You can get the whole sequence (which could be different from mine in your terminal, by the way) from insert mode by hitting
Ctrl+VCtrl+PageUp; however, given the meaning of ^[, you can also use Ctrl+VEscape and then type [5^ manually.
Unfortunately, putting set <C-PageUp>=^[[5^ causes error E518. I don't know why.
On the other hand, another solution is the following (again described here)
set <F37>=^[[5^
nnoremap <F37> :echo "ciao"<CR>
where <F37> is one of the extra function keycode Vim provides. I have no clue where this thing is in the :help.

How to use v:count in Vim to switch line with line n lines away?

I would like to create a mapping in vim that would switch the current line with a line that is n lines away.
So far, I have something like this:
:nnoremap gl:<C-U> dd{v:count1}jO<Esc>pjdd{v:count1}o<Esc>p
But it does not work. How can I use the v:count variable in this context?
Mappings are evaluated exactly as typed. Things like v:count are special Vimscript variables, so its value somehow needs to be interpolated into the keys of the right-hand side. With mappings, there are two possibilities:
execute
With :execute, you can break out from the normal mode (started by :nnoremap) into Ex mode and assemble the command; you need :normal! to run the normal mode command(s) that were previously just put as the right-hand side. As Vim automatically translates any [count] given to the mapping into an Ex range, <C-u> is necessary to clear that.
nnoremap <silent> gl :<C-u>execute 'normal! yyma' . v:count1 . "jVp'aVp"<CR>
map-expr
The <expr> modifier adds a layer of indirection to mappings: the right-hand side is evaluated and only the result is taken as the commands to run. There's a similar complication with [count] here; a preceding <Esc> command cancels it (so you only yank a single line).
nnoremap <expr> gl '<Esc>yyma' . v:count1 . "jVp'aVp"
your implementation
I assume you chose gl as the left-hand side, and the :<C-U> is an attempt at escaping, like I've done with my first approach. The space that separates left-hand side from right-hand side is missing there, and the :normal, too.
I've made the following changes to the implementation:
Deleting and pasting next to it complicates the counting; instead, I've used paste over a visual selection (Vp); this automatically puts the removed text into the unnamed register (so it can be reused for the second part of the swap).
Instead of using the count a second time to jump back, I've set a mark (ma) for simplicity. You might also be able to employ the jump list (<C-o>) for this.
other implementations
For simple swaps, your implementation might suffice, but it's far from perfect. There might be a desire to swap multiple (e.g. visually selected) lines, redo a swap elsewhere with ., and check that the destination line actually exists.
The unimpaired.vim plugin has a swap mapping [e. My LineJuggler plugin also has a robust and universal implementation (as well as many other related ones).

How to jump to a search in a mapped :normal command?

What do you need to properly jump to a matched search result?
To reproduce, make a macro with a search in it after you've run vim -u NONE to ensure there's no vimrc interfering. You'll need to make a file with at least 2 lines and put the cursor on the line without the text TEST_TEXT.
map x :norm gg/TEST_TEXT^MIthis
My intention is that when I press x, it goes to the top of the file, looks for TEST_TEXT and then puts this at the start of the line that matches the search. The ^M is a literal newline, achieved with the CtrlQ+Enter keypress. What's happening instead is either nothing happens, or the text gets entered on the same line as when I called the macro.
If I just run the :norm gg/TEST_TEXT^MIthis command without mapping it to a key, the command executes successfully.
I had an initially longer command involving a separate file and the tcomment plugin, but I've gotten it narrowed down to this.
What is the correct sequence of keys to pull this off once I've mapped it to a key?
The problem is that the ^M concludes the :normal Ex command, so your search command is aborted instead of executed. The Ithis is then executed outside of :normal.
In fact, you don't need :normal here at all. And, it's easier and more readable to use the special key notation with mappings:
:map x gg/TEST_TEXT<CR>Ithis
If you really wanted to use :normal, you'd have to wrap this in :execute, like this:
:map x :exe "norm gg/TEST_TEXT\<lt>CR>Ithis"<CR>
Bonus tips
You should use :noremap; it makes the mapping immune to remapping and recursion.
Better restrict the mapping to normal mode, as in its current form, it won't behave as expected in visual and operator-pending mode: :nnoremap
This clobbers the last search pattern and its highlighting. Use of lower-level functions like search() is recommended instead.
There are many ways of doing this however this is my preferred method:
nnoremap x :0/TEST_TEXT/norm! Itest<esc>
Explanation:
:{range}norm! {cmd} - execute normal commands, {cmd}, on a range of lines,{range}.
! on :normal means the commands will not be remapped.
The range 0/TEST_TEXT start before the first line and then finds the first matching line.
I have a few issues with your current mapping:
You are not specifying noremap. You usually want to use noremap
It would be best to specifiy a mode like normal mode, e.g. nnoremap
It is usually best to use <cr> notation with mappings
You are using :normal when your command is already in normal mode but not using any of the ex command features, e.g. a range.
For more help see:
:h :map
:h :norm
:h range
try this mapping:
nnoremap x gg/TEST_TEXT<cr>Ithis<esc>
note that, if you map x on this operation, you lost the original x feature.

Map :w (save) and a call for a .sh file to F2 in VIM

I'd like to map F2 in VIM so that it first saves the file with :w and then calls a script /home/user/proj/script.sh that will upload the changed files to the server.
I already tried
:map F2 :w<cr>|:! /home/user/proj/script.sh
but that doesn't work.
Please tell my why this isn't working and help me to get this working.
Try :noremap <F2> :<c-u>update <bar> !/home/user/proj/script.sh<cr>.
noremap vs. map
This creates a non-recursive mapping. See Wikia - Mappings keys in Vim
I just assumed this, because it's what people want most of the time. :-)
update vs. write
Only save the file if there were actual changes. See: :help :update.
<bar> vs. |
:help map_bar
<c-u>?
You used a generic mapping, instead of one for a certain mode, like nmap for normal mode mappings. Thus the mapping could also be triggered in visual mode. If you select something in visual mode and hit :, you'll see the commandline is prefixed with a range. But you don't want that in your case and <c-u> clears the commandline.
<cr> at the end?
Your mapping drops into the commandline mode and inserts 2 things separated by <bar>. Afterwards it has to execute what it has written, thus you need to append a <cr>.

Combine two vim commands into one

I'm having trouble combining two vim commands, specifically <C-w>s and <leader>x into <leader>r (i.e. split window and open spec counterpart of current file). Any help?
Thanks!
It would help if you'd post what exactly you've tried that didn't work. Generally, doing what you describe should be simple. It should be enough to put this in your .vimrc file:
nmap <leader>r <c-w>s<leader>x
This maps <leader>r to expand to the key sequence <c-w>s<leader>x. Note that these are not "commands", as you call them in your question, they're "mappings". A "command" is something completely different in vim, you can read up on that with :help user-commands.
One thing to be careful of is using nmap instead of nnoremap. The command nmap maps the sequence on the left to the sequence on the right while re-using mappings that have already been defined. On the other hand, nnoremap creates a mapping with the original meanings of the keys, so in your case won't work (since <leader>x is defined by some plugin). This is one possible reason you may have failed while trying to do it, but I can't tell from your question.

Resources