Skip to content

Tutorial

DJMcMayhem edited this page Apr 25, 2017 · 7 revisions

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.

1.0, The Basics

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.

1.1, Normal and Insert mode

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!

1.2, Motions

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.

Words

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, seperated 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'

Various other motions

Here are some other various motions that you will find very useful:

$   " To the end of the current line
|   " To the beggining 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 occurence 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 occurence of "char", and 'T' will move the cursor backwards until right after the previous occurence of "char".

Counts

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.

1.3, Operators

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 everyhting 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 everyhting 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 everyhting 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)
Clone this wiki locally