What does #/="" do in Vim? - vim

The symbol / is forward research, so I guess it means: what is stored is the forward research (i.e. /) register (#) needs to be replaced (=) by the empty string (""). Is it correct?
Any other registers like this one?

On its own, #/="" means nothing.
If you prepend it with :help :let:
:let #/=""
then it means "set the #/ register to an empty string", which is a way of emptying it.
You can do that with any writable register. See :help registers to figure out which ones are writable and which ones are read-only.
Example uses:
" put the name of the current file in the system clipboard
:let #+ = #%
" put the last Ex command in the system clipboard
:let #+ = #:
" put the last Ex command in register a for later use
:let #a = #:
Note that this is a quick and dirty method. :help setreg() is much cleaner but it is too verbose for casual use.

Related

Search and replace contents of yanked text in vim

In my vimrc, I have a shortcut to copy the filename with its path to the clipboard.
:nmap cp :let #* = expand("%")
This works fine. Now, I want to replace the contents of this yanked text to
1) replace \ with /
2) remove certain words from the yanked text.
I am familiar with search and replace on regular text but I don't know how to change my vimrc entry to do this every time on the yanked text when I use my shortcut.
So, something like this?
:nmap cp :let #* = expand("%") || %s/\\/\/ || %s/<word>//
I am using gvim on windows.
The :substitute command works on the buffer contents itself; that's not so useful here. (You could temporarily :put the register / file name, transform it, and then :delete it (back) into a register.) Fortunately, there's an equivalent low-level :help substitute() function that you can apply on a String:
:nnoremap cp :let #* = substitute(expand("%"), '\\', '/', 'g')<CR>
In fact, expand() directly supports a special substitution :help filename-modifiers, so this would be a (rather obscure) solution, too:
:nnoremap cp :let #* = expand("%:gs?\\?/?")<CR>
For the additional removal of <word>, you can use another layer of substitute() / append another :gs???. I'll leave that to you.
Additional critique
Your mapping is missing the trailing <CR> to conclude command-line mode.
You should use :noremap; it makes the mapping immune to remapping and recursion.
% is relative to the current working dir. If you need the full absolute path, you can get that via %:p.
Starting your mapping with c is unconventional. It's fine here, because the change command wants a {motion}, and p is not a valid one. In general, I'd rather avoid such clever overloading (as long as you have other unused mapping prefixes still available).

How to append text to end of current line using Vim function?

I'd like to append text to the end of a current line in Vim. I'd like to do this within the context of a function.
How can this be done? Do I need to escape/sanitise the text?
You could use the normal command with the execute command:
let text_for_appending = ' # a comment'
execute "normal! A" . text_for_appending
The exclamation mark is included to prevent any key mappings from being expanded. See :help :normal for more details.
With :exe + :normal! you may need to sanatize the text as you feared -- it'll depend on the kind of quotes you use, and on whether you forget to bang :normal and you have insert mode mappings and abbreviations.
With setline('.', getline('.') . text), vim won't try to interpret the text you append. This seems convoluted, but this is the more robust way to proceed -- it can become way more convoluted if you start to escape things with A.

vim: Bind leader key to open .vimrc in a different path

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>

Search forward for the [count]'th occurrence of the text selected in visual mode

I want to search, like I do with the * command, for a pattern I have selected in visual mode.
I am aware of visual mode yanking, which fills the register 0 by default, and the possibility of just searching by / and then Ctrl-R (retrieving) the contents of register 0 (Ctrl-R, 0) to paste the pattern as a search.
Thing is, I do not want to YANK first, I already have something yanked, I just want to search for what's selected in visual mode now.
How can I do that, please? Can I do that without fiddling with different "yank to register N" tricks?
If you use gvim or console vim built with X support (check if 'guioption' is available) and a is present in your 'guioptions', then you can get current selection from * register. Otherwise, I'm afraid there is no easy way to do that without writing a VimL function, which will extract the selection based on values of < and > marks. That function then can be used with CTRL-R = in the search prompt.
Why don't you just combine all the steps you've outlined into a mapping? The only thing missing is saving and restoring the unnamed register, and a little bit of escaping.
" Atom \V sets following pattern to "very nomagic", i.e. only the backslash has special meaning.
" As a search pattern we insert an expression (= register) that
" calls the 'escape()' function on the unnamed register content '##',
" and escapes the backslash and the character that still has a special
" meaning in the search command (/|?, respectively).
" This works well even with <Tab> (no need to change ^I into \t),
" but not with a linebreak, which must be changed from ^M to \n.
" This is done with the substitute() function.
" gV avoids automatic reselection of the Visual area in select mode.
vnoremap <silent> * :<C-U>let save_unnamedregister=##<CR>gvy/\V<C-R><C-R>=substitute(escape(##,'/\'),"\n",'\\n','ge')<CR><CR>:let ##=save_unnamedregister<Bar>unlet save_unnamedregister<CR>gV
Here's the solution that works for me to make * work with [count] in visual mode:
vnoremap * :call <SID>VisualSearch()<cr>:set hls<cr>
fun! s:VisualSearch() range
let unnamed = #"
let repeat = v:count
exe 'norm gv"zy' | let #/ = #z
for x in range(repeat)
call search(#/, 'ws')
endfor
let #" = unnamed
endfun
You change the "z"s on line five to whatever registers you never use.

Indenting entire file in Vim without leaving current cursor location

I already know that gg=G can indent the entire file on Vim. But this will make me go to the beginning of the file after indent. How can I indent the entire file and maintain the cursor at the same position?
See :h ''
This will get you back to the first char on the line you start on:
gg=G''
and this will get you back to the starting line and the starting column:
gg=G``
I assume the second version, with the backtick, is the one you want. In practice I usually just use the double apostrophe version, since the backtick is hard to access on my keyboard.
Add this to your .vimrc
function! Preserve(command)
" Preparation: save last search, and cursor position.
let _s=#/
let l = line(".")
let c = col(".")
" Do the business:
execute a:command
" Clean up: restore previous search history, and cursor position
let #/=_s
call cursor(l, c)
endfunction
nmap <leader>> :call Preserve("normal gg>G")<CR>
You can also use this on any other command you want, just change the argument to the preserve function. Idea taken from here: http://vimcasts.org/episodes/tidying-whitespace/
You can set a bookmark for the current position with the m command followed by a letter. Then after you run the indent command, you can go back to that bookmark with the ` (backtick) command followed by the same letter.
In a similar spirit to Alex's answer I use the following mapping in vimrc.
nnoremap g= :let b:PlugView=winsaveview()<CR>gg=G:call winrestview(b:PlugView) <CR>:echo "file indented"<CR>
by pressing g= in normal mode the whole buffer is indented, and the scroll/cursor position is retained.
Following on top of Herbert's solution, the reader can also use <C-o>
In vim script
exe "norm! gg=G\<C-o>"
Or mapping
:nnoremap <F10> gg=G\<C-o>

Resources