-
Notifications
You must be signed in to change notification settings - Fork 9
Tutorial
V is an evolution of vim, which is most definitely not a programming language. It's an editor. Because of this, programming in V will be unlike any other language you have learned before. There are some terminologies you'll need to learn (buffer, cursor, movements, operators, modes etc.), and using it can be confusing or difficult at times. However, it's also extremely fun to program in.
If you are unfamiliar with vim, the best way to get started learning V is by learning vim. If you are on OS X or Linux, it is probably already installed for you. If you are on Windows, you can install it from here. Once you have vim installed, type vimtutor
at the commandline to start learning vim. This is roughly a 20 minute lesson, and goes over the basics of editing text in the vim editor.
If you are familiar with vim, the best way to start learning V is to go over the official list of commands.
If you would rather not install or learn vim, you may also go over this tutorial, which may not be as complete as vimtutor, but will approach things in a more code-golf oriented way.
First, let's talk about how V takes input and output. It's really quite simple. At the start of a V program, all input is loaded into the buffer, which is really just something that holds text. Every character is treated exactly the same, so newlines/quote characters, whitespace etc. take up exactly the same space as any other character. Because of this, V is a 2D language; if the input is two lines, two lines are loaded into the buffer.
During the program, most of the commands will cause the text in the buffer to change in someway.
Then, when the program is done running, all of the text in the buffer is printed. This leads to one of the cool properties of V, and our first program: The cat program. In V, the empty program is a cat program. All input is implicitly sent to the buffer, nothing changes the buffer, and then everything in the buffer is implicitly printed.
Vim (and by extension, V) has four modes: Normal, Insert, Command, and Visual. What this means, is that every character does something different depending on what mode V is currently in. To start with, we'll focus on the two modes that are most useful and important: Normal and Insert. Normal mode is the default. When the program starts it is in normal mode, most of the powerful commands are in normal mode, and before the program ends V implicitly leaves whatever mode it is in to return to normal mode. In normal mode, every keystroke is a command of sorts, whether it's a motion, an operator, or a key that sends you to a new mode. Once you enter insert mode, these keys stop being commands, and start being characters. In fact, in insert mode every single printable ASCII character is just that, a character. This allows us to write things into the buffer using insert mode.
Let's try it out. To enter insert mode from normal, we will use the i
command (Mnemonic: insert). So the following program will print "Hello world!"
iHello world!
While 'i' is the primary command used to enter insert mode, there are many others. For example, any one of these characters will cause you to enter insert mode, but with different side effects:
i " insert
I " INSERT
a " append
A " APPEND
o " open
O " OPEN
C " CHANGE (to end of line)
s " Substitute (current character)
S " SUBSTITUTE (whole line)
R " REPLACE mode
Try playing around with them and some input to see how they affect the location of the inserted text!
This leads to the next thing you need to know about V: The cursor. Try changing the following program from iHello
to aHello
and then AHello
and watch what changes. You'll notice that the location of the text being entered varies; 'i' puts it at the beginning, 'a' puts it after the first input character, and 'A' puts it after all of the input characters. This is because V has a cursor, which is literally exactly the same thing as the cursor you are used to. It defines where text is entered, and where normal mode commands have their affects. But there is one important note:
In Normal mode, the cursor is on a character, in Insert mode the cursor is between characters.
For example, if the text was like this:
Hello, World!
Cursor: ^
Then the cursor is literally on top of the 'r' character. Normal mode commands will start their affects on the 'r' character. (Unless it's a linewise command, but we'll get to that later. One thing at a time!) But if we press 'i', the cursor will be where the bar is:
Hello, Wo|rld!
And if we had pressed 'a', the cursor would be here:
Hello, Wor|ld!
And if we had pressed 'A', the cursor would be here:
Hello, World!|
For completeness sake, the 'I' command would put the cursor here:
|Hello, World!
'I' puts the cursor on the first non-whitespace character, and then enters insert mode. So if there was leading whitespace...
Four leading spaces!
Cursor: ^
Pressing 'I' would put the cursor here:
|Four leading spaces!
Some Normal-mode commands can move the location of the cursor without having any effect on the text in the buffer. These are called "motions". The most basic motions are 'h', 'j', 'k', and 'l'. These correspond to:
h " Left
j " Down
k " Up
l " Right
This might seem like a very strange choice of keys. But there is actually historical precedence for using these particular keys as cursor keys! This question on the Vi/Vim stack exchange covers it very nicely.
Another very useful motion is w
, which will move forward one word. As far as vim is concerned, a "word" is a sequence of letters, digits and underscores. So, for example, "test_test_123_foo" is a single word, and w
will move past all of it, but "hello.world" is three words, "hello", ".", and "world".
hello.world foo
start ^ ^^ ^
first 'w'--|| |
second 'w'--| |
third 'w'---------|
Sometimes this behavior is not desired. In that case, you can move by WORDS, not words. In Vim, a WORD is any sequence of non-whitespace characters, separated by whitespace. Going back to our previous example:
hello.world foo
start ^ ^
first 'W'---------|
Other motions operate on words too. Here are all of the word-based motions:
w " One 'word' forward
W " One 'WORD' forward
b " One 'word' backward
B " One 'WORD' backward
e " To the end of the current 'word'
E " To the end of the current 'WORD'
Here are some other various motions that you will find very useful:
$ " To the end of the current line
| " To the beginning of the current line
^ " To the first non-whitespace character of the current line
+ " To the first non-whitespace character on the next line
- " To the first non-whitespace character on the previous line
G " To the last line in the buffer
H " To the first line in the buffer (V-specific)
L " To the end of the last line in the buffer (V-specific)
If you are familiar with vim, you might have noticed that I recommend |
to go the first character on the current line. In vim, it is generally recommended to use 0
instead. In V, this sometimes works. But in general I would highly recommend avoiding '0' because it is partially deprecated for reasons I will get into later. |
is functionally equivalent (but more powerful).
There are some more custom motions (custom as in they work in V but not in vim) that you can find on this wiki-page.
Another set of motions that are useful are the 'f' and 't' motions. The mnemonics are "(f)ind" and "(t)il" (as in, until). Both of these motions take an argument, a character, and move forward until the next occurrence of that character. 'f' will move the cursor on to that character, and 't' will move the cursor to right before that character. For example:
Hello world
Start ^ ^^
'tw' ------||
'fw' -------|
The uppercase variations of these characters work the same, but in reverse. So 'F' will move the cursor backwards until the previous occurrence of "char", and 'T' will move the cursor backwards until right after the previous occurrence of "char".
One interesting thing about motions is that nearly every single one of them takes a count. So if you would like to move the cursor 5 keys to the right, you could do lllll
, but it's much more efficient to do 5l
. This is something very important to remember, since the concepts of counts will come up many different times as you use V.
Here is where everything comes together, and the beauty of of vim starts to really become obvious. An operator is a command that changes text, but the interesting thing is that none of them do anything on their own. For example, d
is an operator that stands for 'delete', but on it's own, it does nothing. That's because every operator takes a motion as an argument. Let's look at some examples:
dw " Delete a word
d$ " Delete everything until the end of this line
dfw " Delete everything up to (and including) the next 'w' on this line
dj " Delete one line down (this current line and the next line)
dl " Delete one letter
In each of these cases, the second character (or two) behaves exactly as if it was a motion, but tells the operator (d) what text to operate on. And this is the same for every operator! For example, the 'y' operator (Mnemonic '(y)ank') can do any of these things (as well as many others):
yw " Yank a word
y$ " Yank everything until the end of this line
yfw " Yank everything up to (and including) the next 'w' on this line
yj " Yank one line down (this current line and the next line)
yl " Yank one letter
Similarly, the duplicate operator, which is ä
, or <M-d>
will make an extra copy of whatever the motion is. So...
äw " Duplicate a word
ä$ " Duplicate everything until the end of this line
äfw " Duplicate everything up to (and including) the next 'w' on this line
äj " Duplicate one line down (this current line and the next line)
äl " Duplicate one letter
And you know how I said that any motion takes a count? This works the same whether the motion is an argument to an operator, or just a plain motion. So rather than:
dwdw "Delete a word, then delete a word again
You could do either of these:
2dw " Two times, delete a word
d2w " Delete two words
They are functionally equivalent whether the count is before or after the operator. There are many many many operators in Vim (and even more in V), but here is a list of the most useful/important ones:
y " Yank {motion} into the unnamed register
d " Delete {motion} (also yanks by default)
c " Delete {motion}, but enter insert mode afterwards (mnemonic "(c)hange")
> " Indent {motion} with one space
< " Un-indent {motion} with one space
ä " Duplicate {motion} (V-specific)
ys " Surround {motion} in {char} (for example, `ysW*` will surround the current WORD in in asterisks) (V-specific)