May 24, 2021 Vim
In this and the next chapter, we'll use Vimscript to implement a fairly complex program. We'll look at something we've never heard of, and we'll connect what we've learned before in action.
In this example study, when you encounter unfamiliar content, you have
:help
with :help.
If you just go for a horse and see the flowers, you'll get nothing.
If you
:grep
you should now spend a minute
:help :grep
:help :make
If you haven't used quickfix window before,
:help quickfix-window
To be concise:
:grep ...
You will run an external grep program with the parameters you give, parse the results, fill the quickfix list, and you will be able to jump to the corresponding results in Vim.
We'll add a "grep operator" to any Vim's built-in (or custom!)
) action to select the text you want to search for, making
:grep
to use.
In writing down the first step in each meaningful Vimscript program, you need to think about a question: "How will it be used by the user?" ” 。 Try to come up with an elegant, simple, intuitive way to call.
This time I'll do the work for you:
<leader>g
w
i{
Some use cases of how you will use it:
<leader>giw
Grep cursor.
<leader>giW
under the Grep cursor.
<leader>gi'
where Grep is currently located.
viwe<leader>g
Visually select a word and expand the selection to the next word, and then Grep.
There are a lot of, a lot of other ways to use it. It looks like it needs to write a lot, a lot of code, but in fact we just need to implement the Operator function and Vim will do the rest.
Before you bury your head in writing down the huge amount of Vimscript, one way you might be able to help is to simplify your goal and achieve it to speculate on the possible "shape" of your final solution.
Let's simplify our goal to "create a map to search for words under the cursor." T
his is useful and should be simpler, so we can get runable results faster.
For now,
<leader>g
map it to .
Let's start with a mapping skeleton and fill it gradually. To execute this command:
:nnoremap <leader>g :grep -R something .<cr>
If you've
:help grep
you can easily understand this command.
We've seen a lot of mappings before, and nothing here is new.
Obviously we haven't done anything yet, so let's polish this mapping step by step until it meets our requirements.
First we need to search for words under the cursor,
something
Execute the following command:
:nnoremap <leader>g :grep -R <cword> .<cr>
Now give it a try.
<cword>
Vim's command-line pattern, Vim replaces it with "the word below the cursor" before executing the command.
You can get capital form (WORD) by using
<cWORD>
To execute this command:
:nnoremap <leader>g :grep -R <cWORD> .<cr>
Now try putting the cursor on
foo-bar
Vim will be grep
foo-bar
of part of it.
Our search section also has a problem: if there are any special shell characters in it, Vim will not hesitate to pass the grep command to the outside. This can cause the program to crash (or worse: make some big mistakes).
Let's see how to make it hang up. E
nter
foo;ls
put the cursor up to perform the mapping. T
he grep command fails, and Vim executes
ls
command!
That must be bad, what if the word includes more dangerous commands than
ls
To solve this problem, we'll call parameters in quotation marks. To execute this command:
:nnoremap <leader>g :grep -R '<cWORD>' .<cr>
Most shells take the single quotes as (largely) literal, so our mapping is now more robust.
There is also a problem in the search section. T
ry this mapping on
that's
It doesn't work because the single quote in the word conflicts with the single quote in the grep command!
To solve the problem, we can use Vim's
shellescape
function.
Read
:help escape()
:help shellescape()
how it works (really simple).
Because
shellescape()
a Vim string, we need to create
execute
with execute.
Start by executing the following command to
:grep
map
:execute "..."
form:
:nnoremap <leader>g :execute "grep -R '<cWORD>' ."<cr>
Give it a try and make sure it works. I
f not, find out the spelling mistake and correct it.
Then execute the following command
shellescape
:nnoremap <leader>g :execute "grep -R " . shellescape("<cWORD>") . " ."<cr>
Try this command on a general word such as
foo
I
t can work. T
ry a single quote word, such as
that's
I
t still doesn't work!
Why is this happening?
The problem is that Vim has already executed shellescape() before expanding special variables on the command line, such as
shellescape()
<cWORD>
So Vim shell-escaped the literal
"<cWORD>"
(do nothing but add a pair of single quotes to it) and connect
grep
command.
You can see it all with your own eyes by executing the following commands.
:echom shellescape("<cWORD>")
Vim will output
'<cWORD>'
N
ote that quotation marks are also part of the output string.
Vim protects it as a shell command parameter.
To solve this problem,
expand()
use the expand() function to force the expansion of
<cWORD>
string, before it is
shellescape
Let's take a look at how this part works.
Move your cursor to a single quote word , such
that's
, and execute the following command:
:echom expand("<cWORD>")
Vim
that's
expand("<cWORD>")
It's
shellescape
section:
:echom shellescape(expand("<cWORD>"))
This time Vim
'that'\''s'
I
f you think it looks ridiculous, you probably haven't felt the calmness of seeing through the crazy forms of shell escape. A
t the moment, there's no need to get tangled up in this.
It is believed that Vim
expand
the output of the expand and escaped it correctly.
So far we've got a completely escaped version of the word under the cursor. I t's time to connect it to our map! Execute the following command:
:nnoremap <leader>g :exe "grep -R " . shellescape(expand("<cWORD>")) . " ."<cr>
Give it a try. This mapping is no longer a problem, even if we use it to search for words with odd symbols.
"Start with a simple Vimscript and change it a little bit until you reach your goal" will be used by you over and over again.
There are also minor issues to deal with before the mapping is complete. F
irst of all, we said we don't want to automatically jump to the first result, so
grep!
R
eplace
grep
Execute the following command:
:nnoremap <leader>g :execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>
Try again and find that nothing happened. V im filled the quickfix window with the results, but we couldn't open it. Execute the following command:
:nnoremap <leader>g :execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>
Now try this mapping and you'll see Vim automatically open the quickfix window with search results.
All we're doing is continuing at the end of the
:copen<cr>
Finally, during the search, we're going to remove all of Vim's grep output. Execute the following command:
:nnoremap <leader>g :silent execute "grep! -R " . shellescape(expand("<cWORD>")) . " ."<cr>:copen<cr>
We're done, give it a try and treat ourselves!
silent
command simply hides the normal output of a command while it is running.
Add the mapping we just made to
~/.vimrc
file.
If you
:help :grep
read it.
Read
:help cword
.
Read
:help cnext
and
help cprevious
.
Modify your grep maps and try them.
Set
:cnext
and :
:cprevious
to make it easier to move between matching content.
Read
:help expand
Read
:help copen
.
Add the heat parameter to the : copen command in the
:copen
to see if the quickfix window can be opened at the specified height.
Read
:help silent