Vim scripting

Start hacking

1
2
let name = "John"
echo "Hello, " . name

You can either put this in a script (script.vim) and run it (:source script.vim), or you can type the commands individually in normal mode as :let and :echo.

Learn by example

1
2
3
4
5
6
7
8
9
10
function! SuperTab()
let l:part = strpart(getline('.'),col('.')-2,1)
if (l:part=~'^\W\?$')
return "\<Tab>"
else
return "\<C-n>"
endif
endfunction

imap <Tab> <C-R>=SuperTab()<CR>

Here‘s another example with functions, variables and mapping.

Variables

Defining

1
let var = "hello"

Variable prefixes

1
2
3
let g:ack_options = '-s -H'    " g: global
let s:ack_program = 'ack' " s: local (to script)
let l:foo = 'bar' " l: local (to function)

The s: prefix is also available in function names. See :help local-variables

Other prefixes

1
2
3
4
let w:foo = 'bar'    " w: window
let b:state = 'on' " b: buffer
let t:state = 'off' " t: tab
echo v:var " v: vim special
1
2
let @/ = ''          " @  register (this clears last search pattern)
echo $PATH " $ env

Vim options

1
2
3
4
echo 'tabstop is ' . &tabstop
if &insertmode
echo &g:option
echo &l:option

Prefix Vim options with &

Operators

1
2
a + b             " numbers only!
'hello ' . name " concat
1
2
3
let var -= 2
let var += 5
let var .= 'string' " concat

Strings

Strings

1
2
3
4
5
6
7
let str = "String"
let str = "String with \n newline"

let literal = 'literal, no \ escaping'
let literal = 'that''s enough' " double '' => '

echo "result = " . re " concatenation

Also see :help literal-string and :help expr-quote.
See: Strings

String functions

1
2
3
4
5
6
7
8
9
10
11
strlen(str)    " length
len(str) " same
strchars(str) " character length

split("one two three") "=> ['one', 'two', 'three']
split("one.two.three", '.') "=> ['one', 'two', 'three']

join(['a', 'b'], ',') "=> 'a,b'

tolower('Hello')
toupper('Hello')

Also see :help functions
See: String functions

Functions

Functions

1
2
3
4
5
6
7
" prefix with s: for local script-only functions
function! s:Initialize(cmd, args)
" a: prefix for arguments
echo "Command: " . a:cmd

return true
endfunction

See: Functions

Namespacing

1
function! myplugin#hello()

Calling functions

1
2
call s:Initialize()
call s:Initialize("hello")

Consuming return values

1
echo "Result: " . s:Initialize()

Abortable

1
2
function! myfunction() abort
endfunction

Aborts when an error occurs.

Var arguments

1
2
3
4
5
6
7
8
9
10
11
function! infect(...)
echo a:0 "=> 2
echo a:1 "=> jake
echo a:2 "=> bella

for s in a:000 " a list
echon ' ' . s
endfor
endfunction

infect('jake', 'bella')

See :help function-argument. See: Var arguments

Loops

1
2
3
4
5
for s in list
echo s
continue " jump to start of loop
break " breaks out of a loop
endfor
1
2
while x < 5
endwhile

Custom commands

Custom commands

1
command! Save :set fo=want tw=80 nowrap

Custom commands start with uppercase letters. The ! redefines a command if it already exists.

Commands calling functions

1
command! Save call <SID>foo()
1
2
3
function! s:foo()
...
endfunction

Commands with arguments

1
command! -nargs=? Save call script#foo(<args>)
What What
-nargs=0 0 arguments, default
-nargs=1 1 argument, includes spaces
-nargs=? 0 or 1 argument
-nargs=* 0+ arguments, space separated
-nargs=+ 1+ arguments, space reparated

Flow

Conditionals

1
2
3
4
5
6
7
8
let char = getchar()
if char == "\<LeftMouse>"
" ...
elseif char == "\<RightMouse>"
" ...
else
" ...
endif

Truthiness

1
2
if 1 | echo "true"  | endif
if 0 | echo "false" | endif
1
2
3
4
5
if 1       "=> 1 (true)
if 0 "=> 0 (false)
if "1" "=> 1 (true)
if "456" "=> 1 (true)
if "xfz" "=> 0 (false)

No booleans. 0 is false, 1 is true.
See: Truthiness

Operators

1
2
3
4
if 3 > 2
if a && b
if (a && b) || (c && d)
if !c

See :help expression-syntax.
See: Operators

Strings

1
2
3
4
5
if name ==# 'John'     " case-sensitive
if name ==? 'John' " case-insensitive
if name == 'John' " depends on :set ignorecase

" also: is#, is?, >=#, >=?, and so on

Identity operators

1
2
a is b
a isnot b

Checks if it’s the same instance object.

Regexp matches

1
2
"hello" =~ '/x/'
"hello" !~ '/x/'

Single line

1
2
if empty(a:path) | return [] | endif
a ? b : c

Use | to join lines together.

Boolean logic

1
2
3
if g:use_dispatch && s:has_dispatch
···
endif

Lists

Lists

1
2
3
4
5
6
7
8
let mylist = [1, two, 3, "four"]

let first = mylist[0]
let last = mylist[-1]

" Suppresses errors
let second = get(mylist, 1)
let second = get(mylist, 1, "NONE")

Functions

1
2
3
4
5
6
7
len(mylist)
empty(mylist)

sort(list)
let sortedlist = sort(copy(list))

split('hello there world', ' ')

Concatenation

1
2
let longlist = mylist + [5, 6]
let mylist += [7, 8]

Sublists

1
2
3
4
let shortlist = mylist[2:-1]
let shortlist = mylist[2:] " same

let shortlist = mylist[2:2] " one item

Push

1
2
let alist = [1, 2, 3]
let alist = add(alist, 4)

Map

1
2
call map(files, "bufname(v:val)")  " use v:val for value
call filter(files, 'v:val != ""')

Dictionaries

Dictionaries

1
2
3
4
5
6
7
let colors = {
\ "apple": "red",
\ "banana": "yellow"
}

echo colors["a"]
echo get(colors, "apple") " suppress error

See :help dict

Using dictionaries

1
remove(colors, "apple")
1
2
3
4
5
" :help E715
if has_key(dict, 'foo')
if empty(dict)
keys(dict)
len(dict)
1
2
max(dict)
min(dict)
1
2
count(dict, 'x')
string(dict)
1
map(dict, '<>> " . v:val')

Iteration

1
2
3
for key in keys(mydict)
echo key . ': ' . mydict(key)
endfor

Prefixes

1
keys(s:)

Prefixes (s:, g:, l:, etc) are actually dictionaries.

Extending

1
2
" Extending with more
let extend(s:fruits, { ... })

Casting

1
2
3
str2float("2.3")
str2nr("3")
float2nr("3.14")

Numbers

Numbers

1
2
3
let int = 1000
let int = 0xff
let int = 0755 " octal

See :help Number.
See: Numbers

Floats

1
2
let fl = 100.1
let fl = 5.4e4

See :help Float

Arithmetic

1
2
3
3 / 2     "=> 1, integer division
3 / 2.0 "=> 1.5
3 * 2.0 "=> 6.0

Math functions

1
2
3
4
5
6
7
8
sqrt(100)
floor(3.5)
ceil(3.3)
abs(-3.4)

sin() cos() tan()
sinh() cosh() tanh()
asin() acos() atan()

Vim-isms

Execute a command

1
2
execute "vsplit"
execute "e " . fnameescape(filename)

Runs an ex command you typically run with :. Also see :help execute.
See: Execute a command

Running keystrokes

1
2
3
4
normal G
normal! G " skips key mappings

execute "normal! gg/foo\<cr>dd"

Use :normal to execute keystrokes as if you’re typing them in normal mode. Combine with :execute for special keystrokes.
See: Running keystrokes

Getting filenames

1
2
3
4
5
echo expand("%")      " path/file.txt
echo expand("%:t") " file.txt
echo expand("%:p:h") " /home/you/path/file.txt
echo expand("%:r") " path/file
echo expand("%:e") " txt

See :help expand

Silencing

1
silent g/Aap/p

Suppresses output. See :help silent

Echo

1
2
3
4
5
echoerr 'oh it failed'
echomsg 'hello there'
echo 'hello'

echohl WarningMsg | echomsg "=> " . a:msg | echohl None

Settings

1
2
3
4
5
set number
set nonumber
set number! " toggle
set numberwidth=5
set guioptions+=e

Prompts

1
2
let result = confirm("Sure?")
execute "confirm q"

Built-ins

1
2
3
4
5
6
7
8
9
has("feature")  " :h feature-list
executable("python")
globpath(&rtp, "syntax/c.vim")

exists("$ENV")
exists(":command")
exists("variable")
exists("+option")
exists("g:...")

Mapping

Mapping commands

1
2
3
4
5
6
7
8
9
nmap
vmap
imap
xmap
nnoremap
vnoremap
inoremap
xnoremap
...

Explanation

1
[nvixso](nore)map
1
2
3
4
│       └ don't recurse
│
└ normal, visual, insert,
eX mode, select, operator-pending

Arguments

| <buffer> | only in current buffer |
| <silent> | no echo |
| <nowait> | |

Syntax

Highlights

1
2
3
4
5
hi Comment
term=bold,underline
gui=bold
ctermfg=4
guifg=#80a0ff

Filetype detection

1
2
3
4
5
augroup filetypedetect
au! BufNewFile,BufRead *.json setf javascript
augroup END

au Filetype markdown setlocal spell

Conceal

1
2
3
set conceallevel=2
syn match newLine "<br>" conceal cchar=}
hi newLine guifg=green

Region conceal

1
2
3
syn region inBold concealends matchgroup=bTag start="<b>" end="</b>"
hi inBold gui=bold
hi bTag guifg=blue

Syntax

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
syn match :name ":regex" :flags

syn region Comment start="/\*" end="\*/"
syn region String start=+"+ end=+"+ skip=+\\"+

syn cluster :name contains=:n1,:n2,:n3...

flags:
keepend
oneline
nextgroup=
contains=
contained

hi def link markdownH1 htmlH1

Include guards

1
2
3
4
5
6
7
if exists('g:loaded_myplugin')
finish
endif

" ...

let g:loaded_myplugin = 1