May 24, 2021 Vim
So far, we've completed a prototype, and it's time to expand it and make it stronger.
Remember: our initial goal was to create a "grep operator." We also need to do a whole bunch of new things to achieve our goals, but as in the previous chapter: start with something simple and improve step by step until it meets our needs.
Before you begin, comment out the
~/.vimrc
chapter in .vimrc.
We'll also use the same shortcuts to map the new operator.
Creating a new operator requires a lot of commands, and hitting them out by hand will quickly turn into a torture. Y
ou can attach it
~/.vimrc
but let's create a separate file for this operator.
We have enough to do so.
First, find your Vim
plugin
folder. I
n Linux or OS X, this will
~/.vim/plugin
I
f you are a Windows user, it will be located in
vimfiles
directory.
(If you can't find it, use the ':echo $HOME command in Vim) If this folder doesn't exist, create one.
Create
plugin/
grep-operator.vim
T
his is where you place the code for the new operator. O
nce the file is modified, you can execute
:source %
reload the code.
Every time you open Vim, the file is reloaded,
~/.vimrc
Don't forget that before you source, you have to save the file before you can see the change!
To create a new Vim operator, you need to start with two components: a function and a map.
Add the following code to
grep-operator.vim
:
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
function! GrepOperator(type)
echom "Test"
endfunction
Save the file and
:source %
source for it. T
ry to execute the "grep whole word" by pressing the
<leader>giw
Vim will output Test after accepting
iw
Test
which means we've built a skeleton.
The function part is simple, nothing we haven't said. H
owever, the mapping part is complex. W
e first set the
operatorfunc
the function,
g@
to call the function as an operator.
It looks a bit around, but that's how Vim works.
Think of this map as black magic for the time being. You can go to the documentation later to find out exactly.
We've added this operator in norman mode, but we also want to use it in visual mode. Add one more under the previous map:
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
Save and source files. N
ow select something in visual mode and press
<leader>g
Nothing happened, but Vim did output
Test
so our function is already running.
We've
<c-u>
explained what it does. T
ry selecting some text in visual mode and pressing
:
.
Vim will open a command line as usual by pressing : as
:
but the beginning of the command line is automatically added
'<,'>
Vim inserts these texts to make your commands execute within the selected range for efficiency. B
ut this time, we don't need it. W
e use
<c-u>
perform "Remove content from the cursor to the beginning of the line" to remove the excess text.
Finally, there is a lone
:
preparation for calling the
call
command.
We haven't
visualMode()
parameter in the past.
This function is Vim's built-in function, which returns a single-character string to represent the type of visual pattern:
"v"
for character width (characterwise),
"V"
for linewise,
Ctrl-v
for block width (blockwise).
The function we define accepts a
type
parameter.
We know it will be the return value
visualmode()
in visual mode, but in Norman mode?
Edit the body part of the function to make the code look like this:
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
echom a:type
endfunction
Source file, and then continue and test it in a variety of ways. You may get results like this:
viw<leader>g
v
we are in visual mode with character width.
Vjj<leader>g
display
V
we are in visual mode for line width.
<leader>giw
to display
char
because we use the operator in characterwise motion.
<leader>gG
display the
line
because we use the operator in linewise motion.
Now that we know how to distinguish between different kinds of actions, it's important that we choose the words we need to search for.
Our function will need to get the text that the user wants to search for, and the easiest way to do this is to copy it. Modify the function to this:
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
execute "normal! `<v`>y"
elseif a:type ==# 'char'
execute "normal! `[v`]y"
else
return
endif
echom @@
endfunction
Wow. A
lot of new things. T
ry pressing
<leader>giw
<leader>g2e
and
vi(<leader>g
see.
Every time Vim outputs the text included in the action, obviously we're on the right track!
Let's look at this code step by step. F
irst we check the
a:type
statement.
if
If
'v'
it's in visual mode with character width, so we copied the selection Chinese mode.
Note that we use case-sensitive
==#
"
V"
==
be a match if we only use the
"V"
. . and the user sets
ignorecase
and the result will not be as we would like it to be.
Pay attention to defensive programming!
if
second branch of the if statement blocks the action that uses character width in nomal mode.
All that remains is a silent exit. W e simply ignore the visual mode of line width/block width and the corresponding action type. Grep does not search for multiple lines of text by default, so it makes no sense to include line breaks in the search.
Each of our
if
normal!
Command to do two things:
Don't get tangled up in special marking methods first. You'll learn why they're different by the end of this chapter.
The last line of the function
@@
. D
on't
@
the variable that starts with is a register.
@@
"unnamed" register: If you don't specify a register when you delete or copy the text, Vim will put the text here.
To put it simply: We select the text we want to search for, copy it, and then output the text that is being copied.
Now that we have the text we need in the form of a Vim string, we can escape it as we did in the previous chapter.
Modify
echom
command to this:
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
echom shellescape(@@)
endfunction
Save and source the file, and then select the text with special characters in visual mode, press
<leader>g
Vim displays a text that has been escaped and can be safely passed to the shell command.
We can finally add
grep!
C
ommands to implement a real search.
Replace the
echom
line and the code looks like this:
nnoremap <leader>g :set operatorfunc=GrepOperator<cr>g@
vnoremap <leader>g :<c-u>call GrepOperator(visualmode())<cr>
function! GrepOperator(type)
if a:type ==# 'v'
normal! `<v`>y
elseif a:type ==# 'char'
normal! `[v`]y
else
return
endif
silent execute "grep! -R " . shellescape(@@) . " ."
copen
endfunction
It looks familiar. W
e simply execute the last chapter to
silent execute "grep! ..."
command.
Since we no longer cede all the
nnoremap
command, the code is now even clearer and easier to understand!
Save and source files, then try and enjoy the fruits of your hard work!
Because a whole new Vim operator is defined, we can now use it in many scenarios, such as:
viw<leader>g
Select a word in visual mode, and then grep it.
<leader>g4w
four words of Grep.
<leader>gt;
: The text of Grep up to the cent sign.
<leader>gi[
square brackets.
The superiority of Vim is demonstrated here: its editing commands are like a language. When you add a new verb, it automatically doesn't match (most) existing nouns and adjectives.
Read:
:help visualmode()
Read
:help c_ctrl-u
Read
:help operatorfunc
.
Read
:help map-operator