vim cmap lhs only at the beginning - vim

I have a mapping
:cnoremap ch call ShowHistoryMatching
The problem is that the ch characters expand to the right sentence in any case they are typed, no matter if at the beginning or later in the cmap input.
The problem is when I try to search for words in vim using / or ? e.g. for
/cache - it will be expanded using the mapping above.
How can I set the mapping ch to be extended only when it occurs at the beginning of the command?

cmap's are notoriously tricky because they often execute in the wrong context. Some better alternatives:
Use a normal mapping e.g. nnoremap <leader>ch :call ShowHistoryMatching()<cr>
Create a command e.g. command Ch call ShowHistoryMatching()
Use a the cmdalias.vim plugin
Use a more clever abbreviation as described in vim change :x function to delete buffer instead of save & quit post. Similar technique to cmdailias.vim.
Personally I would just create a new command.

You can use the getcmdpos() function to determine if you're at the beginning of the line or somewhere else. This technique can replace a built-in command using an abbreviation or you can adapt it for use in a mapping, possibly with an <expr> mapping.

Related

How do I use Vim's escape() with <C-R> as its first argument?

My goal is to select several words in Vim's visual mode (well, neovim in my case), press leader+L and let fzf show search results for the selected string through :Rg. I came up with this:
vnoremap <expr> <leader>l 'y:<C-U>Rg '. shellescape(escape('<C-R>"', '()[]><')) .'<CR>'
Which does work, but when I select the text options(:modifier) and trigger a search, the escape() command doesn't escape the parentheses and Rg fails to return results.
In short, I'm expecting this command to fire:
:Rg 'options\(:modifier\)'
And I'm getting this instead:
:Rg 'options(:modifier)'
I'm guessing I can't use <C-R> in this context, but I can't seem to figure out why?
UPDATE: Thanks to a helpful reply from user D. Ben Knoble indicating I could drop and construct the mapping differently, I ended up with this, solving my problem:
vnoremap <leader>l "ky:exec 'Rg '. shellescape(escape(#k, '()[]{}?.'))<CR>
You don’t need to—all registers are available as variable’s prefixed with # (all are readable except #_, most are writable, I think).
So instead of <C-R>", use #"

Delete specific lines above current line in vim normal mode? [duplicate]

I have a mapping in my vimrc that downwardly comments out regions of c code:
nmap comc :normal! I//<ESC>
Since the 'normal' ex command implicitly converts input such as "Ncomc" to ".,.+N-1 comc", I can range comments downwardly without many keystrokes and without leaving normal mode. This is, however, a very limited subset of what vim ranges can do. If I'm willing to be verbose, I can achieve upward ranging comments like so:
.,.-5 normal comc
While editing text, I would much prefer to type something like "-6comc" or make a mapping of "Comc" that uses upward ranges. I'm haven't been able to do so successfully.
Similarly, range operations support commenting until a search pattern is reached, e.g :
.,/int main/ comc
I would, however, like to do so without all that typing.
The behavior you requested is normally done with :h map-operator mapping. With this commenting 3 lines down will turn into comc2j though, but 3 lines up is now just as easy: comc2k.
You can also use visual mode without changing your mapping at all: V2kcomc. You will have to add xnoremap with the identical lhs and rhs because nnoremap works only for normal mode. (And do not use nmap.)
Third option is mapping - to something that moves {count} lines up and puts count back:
nnoremap <expr> - (v:count ? ":\<C-u>\n" . (v:count-1) . 'k' . v:count : '')
. This assumes you are writing 6-comc, not -6comc.
// By the way, I would suggest The NERD Commenter if it comes to the plugin.
While it's commendable to go as far as possible without any plugins, sometimes they're just the best option. What will you do when you start working in a language that has comments with # or (*...*)? Add new mappings for these comment characters?
I recommend commentary.vim which does filetype-aware commenting.
The default commenting operator in commentary.vim is gc. You can combine it with motions, and use it in Visual mode too.
Your use cases:
Comment downwards N lines (say, 3): :.,.+3normal gcc, or gc3j or 4gcc.
Comment upwards 5 lines: :.,.-5normal gcc, or simply gc5k.
Comment until int main: :.,/int main/-1normal gcc, or simply gc/int main followed by Enter.

Show comments for specific mappings in .vimrc

I know that I can use :nmap, :vmap, :imap commands to display all the mapping for those 3 modes. However, I would like to display comments that I have for a single mapping.
So suppose I have the following entry in my vimrc:
" Execute current line in bash
nmap <F9> :exec '!'.getline('.')<CR>
This could also look like this:
nmap <F9> :exec '!'.getline('.')<CR> " Execute current line in bash
I would like to have a command that for this mapping that would look something like this:
<F9> Execute current line in bash
As this probably can only be done using custom function, where do I start? How do I parse .vimrc to strip only key mappings and their corresponding comments (I have only done some very basic Vim scripts)?
Do I understand correctly from :help map-comments that I should avoid inline comments in .vimrc?
It's very hard to "embed" comments into a mapping without affecting its functionality. And this would only help with our own mappings, not the ones from plugins.
Rather, I'd suggest to learn from (well-written) plugins, which provide :help targets for their (default) key mappings. So, to document your mapping(s), create a help file ~/.vim/doc/mymappings.txt with:
*<F9>*
<F9> Execute current line in bash
After :helptags ~/.vim/doc, you will be able to look up the comments via :h <F9>. By using the help file, you can use syntax highlighting, longer multi-line comments, and even examples, so I think that'a better and more straightforward solution.
Personally, I wouldn't have much use for such a feature for two reasons: the first one, is that I always have a vim session with a note taking buffer that contains all the tips/commands not yet in my muscle memory. Secondly, you can list all the maps using :map, and it's always a great exercise for your brain in learning vim to literally read vim commands.
That being said…
As this probably can only be done using custom function, where do I start?
…well I guess you could imagine using a function with a prototype such as:
AddMap(op, key, cmd, comment)
used like:
call AddMap("nmap", "<F9>", ":exec '!'.getline('.')<CR>", "Execute current line in shell")
which implementation would add key and comment to an array, so that you could list the array to get your own mappings declared. Using a function like ListMap().
let map_list = []
function! AddMap(op, key, cmd, comment)
" add to map list
insert(map_list, [op, key, comment])
" execute map assign
exec op.' '.key.' '.cmd
endfunction
function! ListMap()
for it in map_list
echo it[0] . '\t' . it[1] . '\t' . it[2]
endfor
endfunction
Of course, one could elaborate on the ListMap() function implementation to generate a more "proper" help file that you could open in a buffer. But that's an idea of how you could do an implementation for a feature close to what you're looking for.
N.B.: this snippet is mostly an idea of how I'd tackle this problem, and it's a one shot write that I did not actually try.
How do I parse .vimrc to strip only key mappings and their corresponding comments (I have only done some very basic Vim scripts)?
I don't think that would be a good solution, I think the better way to achieve your goal is my suggestion. Simpler and more explicit, though it won't show off all mappings. But you don't have all your mappings in your .vimrc.
Do I understand correctly from :help map-comments that I should avoid inline comments in .vimrc?
To be more precise you shall not use inline comments after maps, because they will be interpreted part of the mapping.
how to get mapping created by plugins?
Besides, using :map, you just don't. You look at the sources/documenation of your plugins and find out about them. There's no such thing as a reverse lookup from runtime to declaration point.

How can I delete empty lines in motion in Vim

I know how to delete blank lines with commands.
We may visually select the text block first, and then run commands like
s/^$/d to delete all blank lines or %s/^\(\s*\n\)\+/\r to keep only one blank lines.
Can I perform the above using motion, so that I can just press some keys to perform the "delete-motion" without having to enter command-line mode?
Creating a new operator for this is a good idea, but it can be tough to get right.
The operator-user plugin makes that task easy.
Once you have installed operator-user, all you need to do is add two lines to your vimrc, one to define the operator, and one to define your personal mapping to it:
call operator#user#define_ex_command('delete-blanks', 'g/^$/d')
map _ <Plug>(operator-delete-blanks)
This creates a new operator _. Change it to whatever you like best.
Now you can do _3} or _G or _23k to delete the blank lines contained in the motion. Text objects _a}, doubling of the operator 4__, and Visual mode V7j_ are all also supported, as befits a proper operator.
You could use operatorfunc. For example:
Define a function like this in your .vimrc:
function! DeleteEmptyLines(type)
if a:type == 'line'
silent execute ".,'\"g/^$/d"
endif
endfunction
And a mapping:
nnoremap <silent> ,d :set operatorfunc=DeleteEmptyLines<CR>m"g#
,d performs now just like an operator and accepts a (line-based) motion. You can, for example, insert ,d5j or ,dG in normal mode to delete empty lines in the next 5 lines or to the end of file.
You can find more information on how to extend this functionality here:
http://learnvimscriptthehardway.stevelosh.com/chapters/33.html
and of course::h operatorfunc and :h map-operator.
From :h map-operator:
"An operator is used before a {motion} command. To define your own operator
you must create mapping that first sets the operatorfunc option and then
invoke the g# operator. After the user types the {motion} command the
specified function will be called."
well, using motions I don't think you can only delete blank lines.
But you can do it using a mapping:
:nmap <Leader>db :g/^$/d<CR>
The motions help you move one word, one paragraph away... And before the motion you use an operator (d, c...).
So what you'd want is to create a new operator that deletes blank lines within the given motion (or selection). What I gave you is close to that, but you'd have to invent a new operator (and I don't think there's many unbound keys left).
Other vimmers may correct me, but I think the easiest way to create such operators would be to define a map for each motion and bind it to a function.
There isn't a motion that can combine with a delete such that it only deletes blank lines. A motion denotes all the text between the initial and final position, without any gaps. That includes motions which search using regular expressions.

How do I change the way vim autocompletes commands?

I use the :split in vim all the time, I however neveer uses :spelldump or :spell* in general.
Is there any way to make :split be the first commmand to appear when autocompleting on :sp ?
Like I said in the comment you don't need to autocomplete for this particular use case, :sp will do the trick. If you enter part of a builtin command that is ambiguous, Vim prefers one command over the other:
:s could be :substitute, :split, :spelldump, etc., but for Vim it is :s[ubstitute]
:sp could be :split, :spelldump, :sprevious, etc., but for Vim it is :sp[lit]
In the help this is indicated with square brackets around the optional part of the command just as is shown above.
To answer the general question: I don't think you can change the way Vim autocompletes commands. You can define your own shorter command (which must be uppercase), e.g. :command! S split. Or you could define a mapping, e.g. :nnoremap \s :split<CR>. Finally, you could use a builtin normal mode command, which for this particular use case is simply CtrlW followed by S.

Resources