Vimscript is the built-in scripting language for customizing and extending the Vim text editor.
Getting Started
let name = "John"
echo "Hello, " . name
You can put this in a script (script.vim) and source it (:source script.vim), or run individual commands in normal mode directly via :let and :echo.
Learning by Example
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>
Variables
Definition
let var = "hello"
Variable Scope Prefixes
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)
let a:arg = 'baz' " a: function argument
The s: prefix can also be used for script-local function names. See :help local-variables.
Other Scopes
let w:foo = 'bar' " w: window local
let b:state = 'on' " b: buffer local
let t:state = 'off' " t: tab page local
echo v:version " v: vim internal variable
let @/ = '' " @ register (this clears last search pattern)
echo $PATH " $ environment variable
Vim Options
echo 'tabstop is ' . &tabstop
if &insertmode
echo &g:option
echo &l:option
Prefix option names with & to read or modify them in expressions.
Arithmetic & String Operators
a + b " Numbers only!
'hello ' . name " String concatenation
let var -= 2
let var += 5
let var .= 'string' " Append string
Strings
String Syntax
let str = "String"
let str = "String with \n newline"
let literal = 'literal, no \ escaping'
let literal = 'that''s enough' " Double '' produces single '
echo "result = " . res " Concatenation
String Functions
strlen(str) " Byte length
len(str) " Same as strlen
strchars(str) " Character count
split("one two three") "=> ['one', 'two', 'three']
split("one.two.three", '.') "=> ['one', 'two', 'three']
join(['a', 'b'], ',') "=> 'a,b'
tolower('Hello')
toupper('Hello')
Functions
Function Definition
" Prefix with s: for script-local functions
function! s:Initialize(cmd, args)
" a: prefix accesses arguments
echo "Command: " . a:cmd
return 1
endfunction
Overwriting Functions
function! MyFunc()
echo "Function defined or replaced"
endfunction
Using function! (with !) replaces any previously defined function with the same name without throwing an error.
Namespacing & Autoload
function! myplugin#hello()
" Automatically loaded from autoload/myplugin.vim
endfunction
Invoking Functions
call s:Initialize()
call s:Initialize("hello", 123)
Capturing Return Values
let result = s:Initialize()
Abort on Error
function! MyFunction() abort
" Aborts immediately on runtime errors
endfunction
Variadic Arguments
function! Infect(...)
echo a:0 "=> Number of extra arguments (e.g. 2)
echo a:1 "=> First extra argument: 'jake'
echo a:2 "=> Second extra argument: 'bella'
for item in a:000 " a:000 is a list of all variadic arguments
echon ' ' . item
endfor
endfunction
call Infect('jake', 'bella')
Loops & Flow Control
Loops
for s in list
echo s
continue " Skip to next iteration
break " Break out of loop
endfor
while x < 5
let x += 1
endwhile
Custom Commands
Defining Commands
command! Save :set fo=want tw=80 nowrap
User-defined commands must start with an uppercase letter. The ! flag allows replacing an existing command.
Invoking Functions in Commands
command! Save call <SID>Foo()
function! s:Foo()
" Implementation
endfunction
Command Arguments
command! -nargs=? Save call script#foo(<args>)
| Argument Spec | Description |
|---|---|
-nargs=0 |
0 arguments (default) |
-nargs=1 |
Exactly 1 argument (may include spaces) |
-nargs=? |
0 or 1 argument |
-nargs=* |
0 or more whitespace-separated arguments |
-nargs=+ |
1 or more whitespace-separated arguments |
Conditionals
If / Elseif / Else
let char = getchar()
if char == "\<LeftMouse>"
" Left click
elseif char == "\<RightMouse>"
" Right click
else
" Other input
endif
Truthiness
if 1 "=> True
if 0 "=> False
if "1" "=> True (parsed as 1)
if "456" "=> True (parsed as 456)
if "xfz" "=> False (strings not starting with digits evaluate to 0)
Vimscript has no standalone boolean type: 0 is false and nonzero integers are true.
Comparison Operators
if 3 > 2
if a && b
if (a && b) || (c && d)
if !c
Case Sensitivity in Strings
if name ==# 'John' " Explicitly case-sensitive
if name ==? 'John' " Explicitly case-insensitive
if name == 'John' " Dependent on &ignorecase setting
Identity Comparison
if a is b
if a isnot b
Tests if a and b reference the identical data instance.
Regular Expression Matches
"hello" =~ 'xx*'
"hello" !~ 'xx*'
"hello" =~ '\v<\d+>'
\v activates "very magic" regex syntax, allowing characters like <>, +, and {} without extra escaping.
Single Line Statements
if empty(a:path) | return [] | endif
let status = a ? b : c
Use | to chain multiple statements on one line.
Data Structures
Lists
let mylist = [1, "two", 3, "four"]
let first = mylist[0]
let last = mylist[-1]
" Safe index lookup with fallback
let second = get(mylist, 1)
let fallback = get(mylist, 10, "NONE")
List Operations
len(mylist)
empty(mylist)
let sortedlist = sort(copy(mylist))
" Sublists
let shortlist = mylist[2:-1]
let shortlist = mylist[2:] " From index 2 to end
let single = mylist[2:2] " Single item list
" Concatenation & append
let longlist = mylist + [5, 6]
let mylist += [7, 8]
call add(mylist, 9)
Functional List Methods
call map(files, "bufname(v:val)") " Map using v:val
call filter(files, 'v:val != ""') " Filter using boolean expression
Dictionaries
let colors = {
\ "apple": "red",
\ "banana": "yellow"
}
echo colors["apple"]
echo get(colors, "apple") " Suppresses missing key error
Dictionary Operations
call remove(colors, "apple")
if has_key(dict, 'foo')
if empty(dict)
let k = keys(dict)
let v = values(dict)
let n = len(dict)
echo max(dict)
echo min(dict)
" Extending dictionary
call extend(s:fruits, { "cherry": "red" })
for key in keys(mydict)
echo key . ': ' . mydict[key]
endfor
Scope prefixes (s:, g:, l:, b:) are themselves accessible as dictionary objects:
let script_keys = keys(s:)
Numbers & Arithmetic
let int = 1000
let hex = 0xff
let oct = 0755
let fl = 100.1
let sci = 5.4e4
3 / 2 "=> 1 (integer division)
3 / 2.0 "=> 1.5 (float division)
sqrt(100)
floor(3.5)
ceil(3.3)
abs(-3.4)
Vim Specifics
Executing Commands
execute "vsplit"
execute "edit " . fnameescape(filename)
Normal Mode Keystrokes
normal G
normal! G " Ignores custom mappings
execute "normal! gg/foo\<cr>dd"
Filename Expansion
echo expand("%") " Current file path: path/file.txt
echo expand("%:t") " Basename tail: file.txt
echo expand("%:p:h") " Directory full path: /home/user/path
echo expand("%:r") " Root name: path/file
echo expand("%:e") " Extension: txt
Output Control & Messages
silent g/pattern/p " Suppress command output
echo 'Simple text'
echomsg 'Persistent text recorded in :messages'
echoerr 'Error message'
echohl WarningMsg | echomsg "=> Warning!" | echohl None
Configuration Options
set number
set nonumber
set number! " Toggle
set numberwidth=5
set guioptions+=e
Introspection & Feature Detection
has("clipboard") " Check compiled features
executable("python3") " Check binary in PATH
globpath(&rtp, "syntax/c.vim")
exists("$ENV")
exists(":command")
exists("variable")
exists("+option")
Key Mappings
Modes
[nvixso](nore)map
n: Normal modev: Visual and Select modex: Visual mode onlyi: Insert modec: Command-line modeo: Operator-pending modenore: Non-recursive (prevents infinite mapping loops)
Common Flags
| Flag | Meaning |
|---|---|
<buffer> |
Local to current buffer only |
<silent> |
Do not echo command in status line |
<nowait> |
Trigger immediately without waiting for longer mappings |
Syntax Highlighting & Conceal
Highlight Groups
hi Comment term=bold gui=bold ctermfg=4 guifg=#80a0ff
hi def link markdownH1 htmlH1
Filetype Detection
augroup filetypedetect
au! BufNewFile,BufRead *.json setf javascript
augroup END
au Filetype markdown setlocal spell
Conceal
set conceallevel=2
syn match newLine "<br>" conceal cchar=}
hi newLine guifg=green
Include Guards
if exists('g:loaded_myplugin')
finish
endif
let g:loaded_myplugin = 1