Consider the following vimscript which I have added to an ftplugin ("~/.vim/ftplugin/tex.vim"):
vnoremap ,test <Esc>:call TestFunc(input("input:"))<CR>
inoremap ,test <Esc>:call TestFunc(input("input:"))<CR>
fun! TestFunc(name)
execute "normal! i" . a:name
endfun
My problem is that calling this from visual mode adds an initial whitespace, while it doesn't do this in insert mode:
If I type ,test while in insert mode, I get input: in the command line. But if I type ,test in visual mode, I get input: in the commandline (notice the trailing space).
This space is then added as the first character to the argument of TestFunc, so that if I type hello as input, TestFunc adds hello to the document if I started from insert mode, but hello if I started from visual mode.
Why is the whitespace added, and how do I get rid of it?
(I am using MacVim with Vim 8.2.)
:help input() says:
Note: When input() is called from within a mapping it will consume remaining characters from that mapping, because a mapping is handled like the characters were typed.
There is a trailing space on your first line so input() "consumes" it, which makes it the first character of the input text. The solution, here, is simply to remove that trailing space.
The proposed example, with inputsave() and inputrestore(), is a bit over the top for this specific case but it is probably safer to systematically use that pattern.
FWIW, enabling the display of trailing spaces and other special characters is always a good idea: see :help 'list' and :help 'listchars'.
Your first line
vnoremap ,test <Esc>:call TestFunc(input("input:"))<CR>
has a trailing whitespace. Get rid of that, and it will behave as you wish.
I guess the reason is the following. The first part :call TestFunc(input("input:"))<CR> tells Vim to execute the code before <CR>. Vim does the job as told; it shows the input prompt, and remembers to call TestFunc() once the input is given. But then, you (i.e., the vnoremap) strikes one more key: . This is like you're striking the spacebar when the input prompt is active. You can test this by replacing the trailing whitespace with something else.
Related
I have my .vimrc in a different path, that I source from my main ~/.vimrc (so I can share same settings across Windows, bash on Windows, etc).
I'm trying to write something in the .vimrc in question, that would make a hotkey for editing said .vimrc, without hard coding the path.
What I currently have is this:
let g:vimrc_path = expand('<sfile>')
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
But this doesn't seem to do anything. I've verified that g:vimrc_path is the right value, and that the <Leader>v ends up being called by subbing in echo messages, but I'm not wrapping my head around why the variable I'm trying to define doesn't get expanded correctly.
String concatenation is done with ., not with +, which performs coercion into numbers and addition. But :execute takes multiple arguments (which it space-separates), so you don't actually need this here.
You should use :noremap; it makes the mapping immune to remapping and recursion.
Also, I doubt you need visual and operator-pending modes (:help map-modes), so define this just for normal mode.
:exec[ute is an Ex command, so for a normal-mode mapping, you need to first enter command-line mode. So :exec 'edit' instead of exec ':edit'.
Also, this is not a function (though Vim 8 now also has execute()), so the parentheses are superfluous.
The <silent> avoids the printing of the whole command (you'll notice the loading of the vimrc file, anyway); it's optional.
The fnameescape() ensures that pathological path names are also handled; probably not necessary here.
let g:vimrc_path = expand('<sfile>')
nnoremap <silent> <Leader>v :execute 'edit' fnameescape(g:vimrc_path)<CR>
Alternative
As the script path is static, you can move the variable interpolation from runtime (mapping execution) to mapping definition, and get rid of the variable:
execute 'nnoremap <Leader>v :edit' fnameescape(expand('<sfile>')) . '<CR>'
Strings in vimscript are concatenated with ., not with +. For example:
:echo "Hello"." world!"
will echo
Hello world!
If you were to type
:echo "Hello" + " world!"
vim would echo
0
This is because the + operator is only for numbers, so vim attempts to cast these strings to numbers. If you were to run
:echo "3" + "1"
vim would echo "4".
So basically, you just need to change
:map <Leader>v exec(":e " + g:vimrc_path + "<CR>")
to
:map <Leader>v exec(":e ".g:vimrc_path."<CR>")
Another problem you might have not seen is that "<CR>" evaluates to the literal text "<CR>", so it only messes up your function. If you want a literal carriage return, you would need a backslash. However, you definitely do not want to do this! Seriously, try it out and see.
You can see the issue. It looks for a file that has a literal carriage return at the end of the filename! There is a very simple fix though. Remove the "\<cr>" completely. Since :exec runs ex commands by default, the carriage return (and the colon too for that matter) are unnecessary.
Also, as a nitpick,
The parenthesis are not needed for the "exec" function, and
Use nnoremap instead to avoid recursive mappings.
Taking all of this into consideration, I would simplify it to
:nnoremap <Leader>v :exec "e ".g:vimrc_path<cr>
In vim I'd like to use a hotkey to enter a mapping command, which in turn maps a hotkey to a string of commands. Here's what I'm doing:
nnoremap <leader>r :map `t :w<cr>:silent !make<cr>:redraw!<cr>
As you can see, when I press <leader>r, vim will put the mapping command on the command line and I can modify the actual command (in this case make) then create the mapping by pressing enter.
Now, this doesn't really work because the <cr>s will apply to the nnoremap command. How do I escape those and have them show up on the command line so they apply to the :map command?
To add <...> literally to a mapping you need to escape the file < with <lt>. So <cr> would become <lt>cr>.
So your mapping would look like
nnoremap <leader>r :map `t :w<lt>cr>:silent !make<lt>cr>:redraw!<lt>cr>
This is covered in the help as a subsection to :h command-bang
Replacement text
The replacement text for a user defined command is scanned for special escape
sequences, using <...> notation. Escape sequences are replaced with values
from the entered command line, and all other text is copied unchanged. The
resulting string is executed as an Ex command. To avoid the replacement use
<lt> in place of the initial <. Thus to include "<bang>" literally use
"<lt>bang>".
The idea is to have a map that allows me to fold blocks of code enclosed in curly braces.
nnoremap zff 0f{zf%
This works as expected but only on the opening brace.
The following version seems to work as long as { and } are on the same line. Should they be on different lines though the cursor only jumps to the opening { and no fold is created.
nnoremap zff 0f{f}zf%
edit:
The problem appears to be that once there is an error in one of the motion commands the map breaks.
:silent! prevents ex commands from breaking a map in case of an error. Is there an equivalent for motion commands?
Your first mapping should work, unless you have a filetype detection or syntax problem that prevents proper pair-matching.
Your second mapping can't work because f (and FtT) only works on the current line. Alternatives:
f{v/}<CR>zf
f{zf/}<CR>
But you can use zf with text-objects:
zfi{
zfat
zfip
so… why bother with a mapping?
You're right that an error in a command sequence breaks the sequence, and this is usually right. If you want to continue even in case of errors, just execute the commands separately through :normal!. This can be chained in a single command-line via :execute:
nnoremap zff :exe 'normal! 0f{' | exe 'normal! f}' | exe 'normal! zf%'
I see <cr>s a lot in vim mappings, but what does it do?
:help key-notation
says:
notation meaning equivalent decimal value(s)
-----------------------------------------------------------------------
<CR> carriage return CTRL-M 13 *carriage-return*
<Return> same as <CR> *<Return>*
<Enter> same as <CR> *<Enter>*
Mappings often involve Ex commands and you must press <CR> to execute them so it's included in the mapping.
Why <special keys>?
While you can use literal keys in mapping definitions (the Enter key would appear as ^M, or even just as an additional new line, depending on the settings), Vim provides a special key notation for key (combinations), so that it is easier to define (you don't have to use i_CTRL-V to literally insert the special character) and understand (<A-a> better expresses the intention than the equivalent á) the mappings.
See :help key-notation for a list and explanation.
Why <CR>?
As many mappings invoke Ex commands (e.g. :w) and therefore have to switch from normal to command-line mode, they have to conclude the command with <Enter> (or <CR>), just as you would when manually typing the command.
The <CR> in vim mappings is the carriage return usually the Enter on your keyboard.
<CR> in a mapping corresponds to the Enter key just like a in a mapping corresponds to the A key. Ley's say you have this mapping
:map <f8> :wq<cr>
This will map F8 to the key sequence :WQEnter (which would save the current buffer and quit).
It's basically a means to say "this is the end", see map:
When you have a mapping that contains an Ex command, you need to put a line
terminator after it to have it executed. The use of is recommended for
this. Example:
:map _ls :!ls -l %<CR>:echo "the end"<CR>
also,
<CR> [count] lines downward, on the first non-blank character |linewise|.
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.