Skip to content

vim script

chimay edited this page Jan 10, 2022 · 63 revisions

Help

As a VimL programmer, the most used help file is :

:help eval.txt

Once there, you can use a plain search.

E.g., searching for buf.*() will give you buffer related functions.

You can also use the completion in command mode :

:help buf*(<tab>

will give you a list of candidates, like bufnr() or bufadd().

The :helpgrep command search in all help files and fill the quickfix list.

Each kind of VimL object has its own syntax :

  • normal mode key : ctrl-a
  • insert mode key : i_ctrl-a
  • visual mode key : v_o
  • command mode key : c_ctrl-d, c_<tab>
  • command : :buffers
  • function : getbufinfo()
  • option : 'option'
  • expression : expr-&&
  • regex pattern : /*
  • vim flag : -p

Finally, the reference :

:help help
:help help-context

Numbers

Float to integer with float2nr() :

let float = 2.01
echo float2nr(float)

Integer to float with round() :

let int = 2
echo round(int)

String to integer with str2nr() :

let string = '   0002  '
echo str2nr(string)

Variables

Local variables

Most of the time, variables used inside a function are local, even without the l: prefix :

let var = 1
let box = 2
fun! Locals ()
	let var = 3
	let l:box = 4
	echo 'var from inside : ' var
	echo 'l:box from inside : ' l:box
	echo 'box from inside : ' box
endfun
call Locals ()
echo 'var : ' var
echo 'box : ' box

The l: can sometimes be useful however, for instance to avoid confusion with a (neo)vim v: variable, like v:count.

Its almost for readability, for the editor seem to see the difference :

fun! VimVars ()
	let l:count = 3
	echo 'l:count : ' l:count
	echo 'count : ' count
	echo 'v:count : ' v:count
endfun

Constants

let box = 3
echo box
" box -> constant
lockvar box
" throws error E741
let box = 4
echo box
" box -> variable
unlockvar box
" ok
let box = 5
echo box

Variable containing code

Ex command

With :execute :

let code = 'let var = range(5)'
execute code
echo var

Expression

With eval() :

let expression = "strftime('%H:%M')"
echo eval(expression)

Can be used to revert string() :

let list = range(10)
let str = string(list)
" returns 1 = string
echo type(str)
let new_list = eval(str)
" returns 3 = list
echo type(new_list)

Normal mode

With :normal :

let delta = 2
execute 'normal! ' . delta . "\<c-o>"

Pointer : variable name

Variable containing the name of another variable :

let var = 'hello'
let ptr = 'var'
echo {ptr}

File system

Test

Test for a regular file :

if filereadable(my_file)
	call do_something (my_file)
endif

Test for a directory :

if isdirectory(my_dir)
	call do_something (my_dir)
endif

File name

Expand the tilde with expand() :

echo expand('~/Documents')

Modify the name with fnamemodify() :

" full path
echo fnamemodify('~/Documents/file', ':p')
echo fnamemodify('Documents/folder/file', ':p')
" directory part (head)
echo fnamemodify('~/Documents/file', ':h')
echo fnamemodify('Documents/folder/file', ':h')
" file part (tail)
echo fnamemodify('~/Documents/file', ':t')
echo fnamemodify('Documents/folder/file', ':t')
" without extension
echo fnamemodify('~/Documents/file.ext', ':r')
echo fnamemodify('Documents/folder/file.ext', ':r')
echo fnamemodify('~/Documents/file.tar.gz', ':r')
echo fnamemodify('~/Documents/file.tar.gz', ':r:r')
" extension
echo fnamemodify('~/Documents/file.ext', ':e')
echo fnamemodify('~/Documents/file.tar.gz', ':e')
echo fnamemodify('~/Documents/file.tar.gz', ':e:e')

Globbing

List of files & dirs in current folder :

echo glob('*', v:false, v:true)

Recursive, all tree of files & dirs in current folder :

echo glob('**', v:false, v:true)

Note the double asterisk.

Tree of vim files :

echo glob('**/*.vim', v:false, v:true)

Buffers

Number & name

Name from number :

echo bufname(1)

Number from name :

let name = bufname(1)
echo bufnr(name)

List of buffers

All buffers :

let buffers = getbufinfo()
for buf in buffers
	echo buf.number ':' buf.name
endfor

Listed buffers :

let buffers = getbufinfo({'buflisted' : 1})
for buf in buffers
	echo buf.number ':' buf.name
endfor

Lines

Current buffer

List of lines in current buffer :

" -- line number, output is a string
" current line
echo getline('.')
" last line
echo getline('$')
" -- range of line, output is a list
echo getline(1, 10)
echo getline('.', '$')

With setline(line, text), you can change a line.

With :put =var, you can add var content below the cursor.

With :put =list, you add an element of list per line.

Any buffer

" alternate buffer
let bufnum = '#'
" -- line number, output is a string
echo getbufline(bufnum, 5)
echo getbufline(bufnum, '$')
" -- range of line, output is a list
echo getbufline(bufnum, 1, 10)
echo getbufline(bufnum, 1, '$')
echo getbufline(bufnum, 1, '$')[4]

See also setbufline(), appendbufline().

Output

Ex command output into a variable

With execute() :

let var = execute('tabs')
echo var
let list = split(var, "\n")
echo list

With :redir :

redir => var
tabs
redir END
let list = split(var, "\n")
echo list

Shell command output into a variable

With system*() :

let var = system('ls -l')
echo var
let list = systemlist('ls -l')
echo list

List content into a file

With writefile() :

let list = ['first line', 'second line', 'third line']
let file = expand('~/Documents/list-file')
" replace content
call writefile(list, file)
" add content
call writefile(list, file, 'a')

With :redir :

let list = ['first line', 'second line', 'third line']
let content = join(list, "\n")
let file = expand('~/Documents/content-file')
execute 'redir! >' file
silent! echo content
redir END
execute 'redir! >>' file
silent! echo content
redir END

Note the distinction between the function execute() and the command :execute

Functions

Name

Variable containing a function name :

let fun = 'strftime'
echo {fun}('%H:%M')

Arguments

Function call(function, argument-list) :

let list = range(5)
let element = 'new'
" add(list, element)
echo call('add', [list, element])
echo list

Optional arguments

Variables linked to optional arguments :

  • a:0 : number of optional args
  • a:1 : first optional arg
  • a:2 : second optional arg
  • ...
  • a:000 : list of optional args

Pass the optional arguments of a function to another one :

fun! Fun (...)
	let var = call('add', a:000)
	return var
endfun
let list = range(5)
echo Fun (list, 7)

References

let list = range(3)
let Fun = function('add')
echo Fun(list, 4)
echo list

With predefined argument(s) :

let list = range(3)
" add(list, element)
let Fun = function('add', [list])
echo Fun(4)
echo Fun(5)
echo list

Lambda

let list = [2, 3, 5, 4, 1]
echo filter(list, {index, value -> value >= 4})
let list = [2, 3, 5, 1, -1]
echo map(list, {index, value -> index + value})

Method

let number = -3.14
echo number->abs()
echo number->abs()->cos()
echo number->abs()->cos()->acos()

Lambda method :

let number = 3
echo number->{ x -> 2 * x }()
echo number->{ x, y -> x + y }(4)

Closure

fun! Fonctional(fn, value)
	fun! Function(arg) closure
		return a:fn(a:arg, a:value)
	endfun
	return funcref('Function')
endfun

let list = range(10)
" add(list, element)
echo Fonctional(function('add'), 3)(list)
let F = Fonctional(function('add'), 5)
echo F(list)

Dict functions

Dict function :

fun! Fundict (arg) dict
	echo self.name a:arg
endfun
" associate it with a dict
let dict = {'name' : 'John'}
let dict.fn = function('Fundict')
" call it
call dict.fn('Doe')
" function ref
let F = function('Fundict', dict)
call F('Smith')
" with empty arg
let G = function('Fundict', [], dict)
call G('Foo')
" with predefined arg
let H = function('Fundict', ['Bar'], dict)
call H()

Anonymous function :

let dict = {'name' : 'John'}
fun! dict.iam (arg) dict
	echo self.name a:arg
endfun
call dict.iam('Will')

Tabs & windows

Introduction

Each window has a :

  • local tab number
  • global unique ID (until you restart vim, of course)

Tab number

let current_tab = tabpagenr()
let last_tab = tabpagenr('$')

Buffer list of a tab

With tabpagebuflist() :

let last = tabpagenr('$')
for tab in range(1, last)
	echo tab ':' tabpagebuflist(tab)
endfor

Local window number

Get id :

let win_nr = winnr()
echo win_nr

Current local number in a given tab :

let tab_nr = 2
let win_nr = tabpagewinnr(tab_nr)
let last_win_nr = tabpagewinnr(tab_nr, '$')
let num_win_on_tab = last_win_nr

Go to it :

let win_nr = 2
execute win_nr 'wincmd w'

Global id

Get id :

let win_local_nr = 2
let tab_nr = 3
let win_id = win_getid(win_local_nr, tab_nr)
echo win_id
let current_win_id = win_getid()
echo current_win_id

Go to id :

let win_id = 1012
call win_gotoid(win_id)

Find a window

Find in which window(s) is displayed a buffer :

let win_list = win_findbuf(buf_nr)

Mappings

Command maps

Of course, you can use :

" command
nnoremap <c-right> :tabnext<cr>
" with <c-u> to avoid things like :'<,'>
nnoremap <c-right> :<c-u>tabnext<cr>
" call function
nnoremap <c-right> :call goto_next_tab()<cr>
" from insert mode
inoremap <c-right> <esc>:tabnext<cr>i

but it’s often better to use <cmd> :

" command, no need to add <c-u>
nnoremap <c-right> <cmd>tabnext<cr>
" call function
nnoremap <c-right> <cmd>call goto_next_tab()<cr>
" from insert mode, no need to surround with <esc>...i
inoremap <c-right> <cmd>tabnext<cr>

Advantages :

  • execute the command without leaving the mode
  • no need to add <c-u> after the colon
  • also works from insert mode without addition
  • faster
  • silent

Plugs

Define a plug :

noremap <plug>(myplugin-function) <cmd>call plugin#file#function()<cr>

and let the user use it for his own map :

map <user-key> <plug>(myplugin-function)

This last map must be recursive to work.

Autocommands

Custom

You can trigger a custom event, somewhere in your plugin :

doautocmd User MyPluginEvent

and let the user add whatever command he wants to it :

autocmd User MyPluginEvent echo 'hello there !'

Control execution

To avoid auto-execution, use noautocmd.

This one change window without triggering autocommand :

noautocmd call win_gotoid(window_id)

If needed, you can trigger them later by using doautocmd :

doautocmd WinEnter
doautocmd BufEnter
	...

No ordinary write

Some buffers are special, and not related to a file, like the quickfix window. In that case, writing it does not mean a simple copy to the file system, but can involve a completely distinct operation. The BufWriteCmd event is there to modify the meaning of the :write command. These autocommands are often buffer local. Example :

autocmd BufWriteCmd <buffer> call FancyStuff ()

Writing the buffer where this autocommand is defined will call FancyStuff() instead of saving to disc.

Yank ring

First define a global var to hold the ring :

let g:yankring = []

then define a function to add yanks :

fun! Add2yankring ()
	let content = getreg('"')
	call insert(yanks, content)
endfun

With this autocommand, your function will be triggered at each yank :

autocmd TextYankPost * call Add2yankring()