summaryrefslogtreecommitdiffhomepage
path: root/vimscript.html.markdown
diff options
context:
space:
mode:
Diffstat (limited to 'vimscript.html.markdown')
-rw-r--r--vimscript.html.markdown672
1 files changed, 672 insertions, 0 deletions
diff --git a/vimscript.html.markdown b/vimscript.html.markdown
new file mode 100644
index 00000000..5235a149
--- /dev/null
+++ b/vimscript.html.markdown
@@ -0,0 +1,672 @@
+---
+language: Vimscript
+filename: learnvimscript.md
+contributors:
+ - ["HiPhish", "http://hiphish.github.io/"]
+---
+
+## Introduction
+
+Vim script (also called VimL) is the subset of Vim's ex-commands which supplies
+a number of features one one would expect from a scripting language, such as
+values, variables, functions or loops. Always keep in the back of your mind
+that a Vim script file is just a sequence of ex-commands. It is very common for
+a script to mix programming-language features and raw ex-commands.
+
+You can run Vim script directly by entering the commands in command-mode (press
+`:` to enter command-mode), or you can write them to a file (without the
+leading `:`) and source it in a running Vim instance (`:source path/to/file`).
+Some files are sourced automatically as part of your configuration (see `:h
+startup`). This guide assumes that you are familiar with ex-commands and will
+only cover the scripting. Help topics to the relevant manual sections are
+included.
+
+See `:h usr_41.txt` for the official introduction to Vim script. A comment is
+anything following an unmatched `"` until the end of the line, and `|`
+separates instructions (what `;` does in most other languages).
+
+```vim
+" This is a comment
+
+" The vertical line '|' (pipe) separates commands
+echo 'Hello' | echo 'world!'
+
+" Putting a comment after a command usually works
+pwd " Displays the current working directory
+
+" Except for some commands it does not; use the command delemiter before the
+" comment (echo assumes that the quotation mark begins a string)
+echo 'Hello world!' |" Displays a message
+
+" Line breaks can be escaped by pacing a backslash as the first non-whitespace
+" character on the *following* line. Only works in script files, not on the
+" command line
+echo " Hello
+ \ world "
+
+echo [1,
+ \ 2]
+
+echo {
+ \ 'a': 1,
+ \ 'b': 2
+\}
+```
+
+
+## Types
+
+For an overview of types see `:h E712`. For an overview of operators see
+`:h expression-syntax`
+
+### Numbers
+See `:h expr-number`
+
+```vim
+echo 123 |" Decimal
+echo 0b1111011 |" Binary
+echo 0173 |" Octal
+echo 0x7B |" Hexadecimal
+echo 123.0 |" Floating-point
+echo 1.23e2 |" Floating-point (scientific notation)
+```
+
+Note that an *integer* number with a leading `0` is in octal notation. The
+usual arithmetic operations are supported.
+
+```vim
+echo 1 + 2 |" Addition
+echo 1 - 2 |" Subtraction
+echo - 1 |" Negation (unary minus)
+echo + 1 |" Unary plus (does nothing really, but still legal)
+echo 1 * 2 |" Multiplication
+echo 1 / 2 |" Division
+echo 1 % 2 |" Modulo (remainder)
+```
+
+### Booleans
+See `:h Boolean`
+
+The number 0 is false, every other number is true. Strings are implicitly
+converted to numbers (see below). There are two pre-defined semantic constants.
+
+```vim
+echo v:true |" Evaluates to 1 or the string 'v:true'
+echo v:false |" Evaluates to 0 or the string 'v:false'
+```
+
+Boolean values can result from comparison of two objects.
+
+```vim
+echo x == y |" Equality by value
+echo x != y |" Unequality
+echo x > y |" Greater than
+echo x >= y |" Greater than or equal
+echo x < y |" Smaller than
+echo x <= y |" Smaller than or equal
+echo x is y |" Instance identity (lists and dictionaries)
+echo x isnot y |" Instance non-identity (lists and dictionaries)
+
+" Strings are compared based on their alphanumerical ordering
+" echo 'a' < 'b'. Case sensitivity depends on the setting of 'ignorecase'
+
+" Explicit case-sensitivity is specified by appending '#' (match case) or '?'
+" (ignore case) to the operator. Prefer explicity case sensitivity when writing
+" portable scripts.
+
+echo 'a' < 'B' | " True or false depending on 'ignorecase'
+echo 'a' <? 'B' | " True
+echo 'a' <# 'B' | " False
+
+" Regular expression matching
+echo "hi" =~ "hello" |" Regular expression match, uses 'ignorecase'
+echo "hi" =~# "hello" |" Regular expression match, case sensitive
+echo "hi" =~? "hello" |" Regular expression match, case insensitive
+echo "hi" !~ "hello" |" Regular expression unmatch, use 'ignorecase'
+echo "hi" !~# "hello" |" Regular expression unmatch, case sensitive
+echo "hi" !~? "hello" |" Regular expression unmatch, case insensitive
+```
+
+Boolean operations are possible.
+
+```vim
+echo v:true && v:false |" Logical AND
+echo v:true || v:false |" Logical OR
+echo ! v:true |" Logical NOT
+echo v:true ? 'yes' : 'no' |" Ternary operator
+```
+
+
+### Strings
+See `:h String`
+
+An ordered zero-indexed sequence of bytes. The encoding of text into bytes
+depends on the option `:h 'encoding'`.
+
+```vim
+" Literal constructors
+echo "Hello world\n" |" The last two characters stand for newline
+echo 'Hello world\n' |" The last two characters are literal
+echo 'Let''s go!' |" Two single quotes become one quote character
+```
+
+Single-quote strings take all characters are literal, except two single quotes,
+which are taken to be a single quote in the string itself. See `:h expr-quote`
+for all possible escape sequences.
+
+```
+" String concatenation
+" The .. operator is preferred, but only supported in since Vim 8.1.1114
+echo 'Hello ' . 'world' |" String concatenation
+echo 'Hello ' .. 'world' |" String concatenation (new variant)
+
+" String indexing
+echo 'Hello'[0] |" First byte
+echo 'Hello'[1] |" Second byte
+echo 'Hellö'[4] |" Returns a byte, not the character 'ö'
+
+" Substrings (second index is inclusive)
+echo 'Hello'[:] |" Copy of entire string
+echo 'Hello'[1:3] |" Substring, second to fourth byte
+echo 'Hello'[1:-2] |" Substring until second to last byte
+echo 'Hello'[1:] |" Substring with starting index
+echo 'Hello'[:2] |" Substring with ending index
+echo 'Hello'[-2:] |" Substring relative to end of string
+```
+
+A negative index is relative to the end of the string. See `:h
+string-functions` for all string-related functions.
+
+### Lists
+See `:h List`
+
+An ordered zero-indexed heterogeneous sequence of arbitrary Vim script objects.
+
+```vim
+" Literal constructor
+echo [] |" Empty list
+echo [1, 2, 'Hello'] |" List with elements
+echo [1, 2, 'Hello', ] |" Trailing comma permitted
+echo [[1, 2], 'Hello'] |" Lists can be nested arbitrarily
+
+" List concatenation
+echo [1, 2] + [3, 4] |" Creates a new list
+
+" List indexing, negative is relative to end of list (:h list-index)
+echo [1, 2, 3, 4][2] |" Third element
+echo [1, 2, 3, 4][-1] |" Last element
+
+" List slicing (:h sublist)
+echo [1, 2, 3, 4][:] |" Shallow copy of entire list
+echo [1, 2, 3, 4][:2] |" Sublist until third item (inclusive)
+echo [1, 2, 3, 4][2:] |" Sublist from third item (inclusive)
+echo [1, 2, 3, 4][:-2] |" Sublist until second-to-last item (inclusive)
+```
+
+All slicing operations create new lists. To modify a list in-place use list
+functions (`:h list-functions`) or assign directly to an item (see below about
+variables).
+
+### Dictionaries
+See `:h Dictionary`
+
+An unordered sequence of key-value pairs, keys are always strings (numbers are
+implicitly converted to strings).
+
+```vim
+" Dictionary literal
+echo {} |" Empty dictionary
+echo {'a': 1, 'b': 2} |" Dictionary literal
+echo {'a': 1, 'b': 2, } |" Trailing comma permitted
+echo {'x': {'a': 1, 'b': 2}} |" Nested dictionary
+
+" Indexing a dictionary
+echo {'a': 1, 'b': 2}['a'] |" Literal index
+echo {'a': 1, 'b': 2}.a |" Syntactic sugar for simple keys
+```
+
+See `:h dict-functions` for dictionary manipulation functions.
+
+### Funcref
+See `:h Funcref`
+
+Reference to a function, uses the function name as a string for construction.
+When stored in a variable the name of the variable has the same restrictions
+as a function name (see below).
+
+```vim
+echo function('type') |" Reference to function type()
+echo funcref('type') |" Reference by identity, not name
+echo {x -> x * x} |" Anonymous function
+echo function('substitute', ['hello']) |" Partial function
+```
+
+A lambda (`:h lambda`) is an anonymous function; it can only contain one
+expression in its body, which is also its implicit return value.
+
+### Regular expression
+See `:h regular-expression`
+
+A regular expression pattern is generally a string, but in some cases you can
+also use a regular expression between a pair of delimiters (usually `/`, but
+you can choose anything).
+
+```vim
+" Substitute 'hello' for 'Hello'
+substitute/hello/Hello/
+```
+
+## Implicit type conversions
+
+Strings are converted to numbers, and numbers to strings when necessary. A
+number becomes its decimal notation as a string. A string becomes its numerical
+value if it can be parsed to a number, otherwise it becomes zero.
+
+```vim
+echo "1" + 1 |" Number
+echo "1" .. 1 |" String
+echo "0xA" + 1 |" Number
+
+" Strings are treated like numbers when used as booleans
+echo "true" ? 1 : 0 |" This string is parsed to 0, which is false
+```
+
+
+## Variables
+
+Variables are bound within a scope; if no scope is provided a default is chosen
+by Vim. Use `:let` and `:const` to bind a value and `:unlet` to unbind it.
+
+```vim
+let b:my_var = 1 |" Local to current buffer
+let w:my_var = 1 |" Local to current window
+let t:my_var = 1 |" Local to current tab page
+let g:my_var = 1 |" Global variable
+let l:my_var = 1 |" Local to current function (see functions below)
+let s:my_var = 1 |" Local to current script file
+let a:my_arg = 1 |" Function argument (see functions below)
+
+" The Vim scope is read-only
+echo v:true |" Special built-in Vim variables (:h v:var)
+
+" Access special Vim memory like variables
+let @a = 'Hello' |" Register
+let $PATH='' |" Environment variable
+let &textwidth = 79 |" Option
+let &l:textwidth = 79 |" Local option
+let &g:textwidth = 79 |" Global option
+
+" Access scopes as dictionaries (can be modified like all dictionaries)
+" See the :h dict-functions, especially get(), for access and manipulation
+echo b: |" All buffer variables
+echo w: |" All window variables
+echo t: |" All tab page variables
+echo g: |" All global variables
+echo l: |" All local variables
+echo s: |" All script variables
+echo a: |" All function arguments
+echo v: |" All Vim variables
+
+" Constant variables
+const x = 10 |" See :h :const, :h :lockvar
+
+" Function reference variables have the same restrictions as function names
+let IsString = {x -> type(x) == type('')} |" Global: capital letter
+let s:isNumber = {x -> type(x) == type(0)} |" Local: any name allowed
+```
+
+When omitted the scope `g:` is implied, except in functions, there `l:` is
+implied.
+
+### Multiple value binding (list unpacking)
+
+```vim
+" Assign values of list to multiple variables (number of items must match)
+let [x, y] = [1, 2]
+
+" Assign the remainer to a rest variable (note the semicolon)
+let [mother, father; children] = ['Alice', 'Bob', 'Carol', 'Dennis', 'Emily']
+```
+
+## Flow control
+
+### Conditional
+
+Conditions are set between `if` and `endif`. They can be nested.
+
+```vim
+if condition
+ echo 'First condition'
+elseif another_condition
+ echo 'Second condition'
+else
+ echo 'Fail'
+endif
+```
+
+### Loops
+
+Two types of loops: `:for` and `:while`. Use `:continue` to skip to the next
+iteration, `:break` to break out of the loop.
+
+#### For-loop
+
+For-loops iterate over lists and nothing else. If you want to iterate over
+another sequence you need to use a function which will create a list.
+
+```vim
+" Iterate over a list
+for person in ['Alice', 'Bob', 'Carol', 'Dennis', 'Emily']
+ echo 'Hello ' .. person
+endfor
+
+" Iterate over a nested list by unpacking it
+for [x, y] in [[1, 0], [0, 1], [-1, 0], [0, -1]]
+ echo 'Position: x =' .. x .. ', y = ' .. y
+endfor
+
+" Iterate over a range of numbers
+for i in range(10, 0, -1) " Count down from 10
+ echo 'T minus' .. i
+endfor
+
+" Iterate over the keys of a dictionary
+for symbol in keys({'π': 3.14, 'e': 2.71})
+ echo 'The constant ' .. symbol .. ' is a transcendent number'
+endfor
+
+" Iterate over the values of a dictionary
+for value in values({'π': 3.14, 'e': 2.71})
+ echo 'The value ' .. value .. ' approximates a transcendent number'
+endfor
+
+" Iterate over the keys and values of a dictionary
+for [symbol, value] in items({'π': 3.14, 'e': 2.71})
+ echo 'The number ' .. symbol .. ' is approximately ' .. value
+endfor
+```
+
+#### While-loops
+
+```vim
+while !there_yet
+ echo 'Are we there yet?'
+endwhile
+```
+
+### Exception handling
+See `:h exception-handling`
+
+Throw new exceptions as strings, catch them by pattern-matching a regular
+expression against the string
+
+```vim
+" Throw new exception
+throw "Wrong arguments"
+
+" Guard against an exception (the second catch matches any exception)
+try
+ source path/to/file
+catch /Cannot open/
+ echo 'Looks like that file does not exist'
+catch /.*/
+ echo 'Something went wrong, but I don't know what'
+finally
+ echo 'I'm done trying'
+endtry
+```
+
+
+## Functions
+
+### Defining functions
+
+```vim
+" Unscoped function names have to start with a capital letter
+function! AddNumbersLoudly(x, y)
+ " Use a: scope to access arguments
+ echo 'Adding' .. a:x .. 'and' .. a:y |" A side effect
+ return a:x + a:y |" A return value
+endfunction
+
+" Scoped function names may start with a lower-case letter
+function! s:addNumbersLoudly(x, y)
+ echo 'Adding' .. a:x .. 'and' .. a:y
+ return a:x + a:y
+endfunction
+```
+
+Without the exclamation mark it would be an error to re-define a function, with
+the exclamation mark the new definition can replace the old one. Since Vim
+script files can be reloaded several times over the course of a session it is
+best to use the exclamation mark unless you really know what you are doing.
+
+Function definitions can have special qualifiers following the argument list.
+
+```vim
+" Range functions define two implicit arguments, which will be set to the range
+" of the ex-command
+function! FirstAndLastLine() range
+ echo [a:firstline, a:lastline]
+endfunction
+
+" Prints the first and last line that match a pattern (:h cmdline-ranges)
+/^#!/,/!#$/call FirstAndLastLine()
+
+
+" Aborting functions, abort once error occurs (:h :func-abort)
+function! SourceMyFile() abort
+ source my-file.vim |" Try sourcing non-existing file
+ echo 'This will never be printed'
+endfunction
+
+" Closures, functions carrying values from outer scope (:h :func-closure)
+function! MakeAdder(x)
+ function! Adder(n) closure
+ return a:n + a:x
+ endfunction
+ return funcref('Adder')
+endfunction
+let AddFive = MakeAdder(5)
+echo AddFive(3) |" Prints 8
+
+" Dictionary functions, poor man's OOP methods (:h Dictionary-function)
+function! Mylen() dict
+ return len(self.data) |" Implicit variable self
+endfunction
+let mydict = {'data': [0, 1, 2, 3], 'len': function("Mylen")}
+echo mydict.len()
+
+" Alternatively, more concise
+let mydict = {'data': [0, 1, 2, 3]}
+function! mydict.len()
+ return len(self.data)
+endfunction
+```
+
+### Calling functions
+
+```vim
+" Call a function for its return value, and possibly for its side effects
+let animals = keys({'cow': 'moo', 'dog': 'woof', 'cat': 'meow'})
+
+" Call a function for its side effects only, ignore potential result
+call sign_undefine()
+
+" The call() function calls a function reference and passes parameters as a
+" list, and returns the function's result.
+echo call(function('get'), [{'a': 1, 'b': 2}, 'c', 3]) |" Prints 3
+```
+
+Recall that Vim script is embedded within the ex-commands, that is why we
+cannot just call a function directly, we have to use the `:call` ex-command.
+
+### Function namespaces
+
+See `:h write-library-script`, `:h autoload`
+
+```vim
+" Must be defined in autoload/foo/bar.vim
+" Namspaced function names do not have to start with a capital letter
+function! foo#bar#log(value)
+ echomsg value
+endfunction
+
+call foo#bar#log('Hello')
+```
+
+
+## Frequently used ex-commands
+
+### Sourcing runtime files
+
+See `:h 'runtimepath'`
+
+```vim
+" Source first match among runtime paths
+runtime plugin/my-plugin.vim
+```
+
+### Defining new ex-commands
+See `:h 40.2`, `:h :command`
+
+```vim
+" First argument here is the name of the command, rest is the command body
+command! SwapAdjacentLines normal! ddp
+```
+
+The exclamation mark works the same as with `:function`. User-defined commands
+must start with a capital letter. The `:command` command can take a number of
+attributes (some of which have their own parameters with `=`), such as
+`-nargs`, all of them start with a dash to set them apart from the command
+name.
+
+```vim
+:command -nargs=1 Error echoerr <args>
+```
+
+### Defining auto-commands
+See `:h 40.3`, `:h autocmd`, `:h autocommand-events`
+
+```vim
+" The arguments are "events", "patterns", rest is "commands"
+autocmd BufWritePost $MYVIMRC source $MYVIMRC
+```
+
+Events and patterns are separated by commas with no space between. See `:h
+autocmd-events` for standard events, `:h User` for custom events. Everything
+else are the ex-commands which will be executed.
+
+#### Auto groups
+
+When a file is sourced multiple times the auto-commands are defined anew,
+without deleting the old ones, causing auto-commands to pile up over time. Use
+auto-groups and the following ritual to guard against this.
+
+```vim
+augroup auto-source |" The name of the group is arbitrary
+ autocmd! |" Deletes all auto-commands in the current group
+ autocmd BufWritePost $MYVIMRC source $MYVIMRC
+augroup END |" Switch back to default auto-group
+```
+
+It is also possible to assign a group directly. This is useful if the
+definition of the group is in one script and the definition of the auto-command
+is in another script.
+
+```vim
+" In one file
+augroup auto-source
+ autocmd!
+augroup END
+
+" In another file
+autocmd auto-source BufWritePost $MYVIMRC source $MYVIMRC
+```
+
+### Executing (run-time macros of sorts)
+
+Sometimes we need to construct an ex-command where part of the command is not
+known until runtime.
+
+```vim
+let line = 3 |" Line number determined at runtime
+execute line .. 'delete' |" Delete a line
+```
+
+### Executing normal-mode commands
+
+Use `:normal` to play back a sequence of normal mode commands from the
+command-line. Add an exclamation mark to ignore user mappings.
+
+```vim
+normal! ggddGp |" Transplant first line to end of buffer
+
+" Window commands can be used with :normal, or with :wincmd if :normal would
+" not work
+wincmd L |" Move current window all the way to the right
+```
+
+
+## Frequently used functions
+
+```vim
+" Feature check
+echo has('nvim') |" Running Neovim
+echo has('python3') |" Support for Python 3 plugins
+echo has('unix') |" Running on a Unix system
+echo has('win32') |" Running on a Windows system
+
+
+" Test if something exists
+echo exists('&mouse') |" Option (exists only)
+echo exists('+mouse') |" Option (exists and works)
+echo exists('$HOSTNAME') |" Environment variable
+echo exists('*strftime') |" Built-in function
+echo exists('**s:MyFunc') |" User-defined function
+echo exists('bufcount') |" Variable (scope optional)
+echo exists('my_dict["foo"]') |" Variable (dictionary entry)
+echo exists('my_dict["foo"]') |" Variable (dictionary entry)
+echo exists(':Make') |" Command
+echo exists("#CursorHold") |" Auto-command defined for event
+echo exists("#BufReadPre#*.gz") |" Event and pattern
+echo exists("#filetypeindent") |" Auto-command group
+echo exists("##ColorScheme") |" Auto-commnand supported for event
+
+" Various dynamic values (see :h expand())
+echo expand('%') |" Current file name
+echo expand('<cword>') |" Current word under cursor
+echo expand('%:p') |" Modifier are possible
+
+" Type tests
+echo type(my_var) == type(0) |" Number
+echo type(my_var) == type('') |" String
+echo type(my_var) == type([]) |" List
+echo type(my_var) == type({}) |" Dictionary
+echo type(my_var) == type(function('type')) |" Funcref
+
+" Format strings
+echo printf('%d in hexadecimal is %X', 123, 123)
+```
+
+
+## Tricks of the trade
+
+### Source guard
+
+```vim
+" Prevent a file from being source multiple times; users can set the variable
+" in their configuration to prevent the plugin from loading at all.
+if exists('g:loaded_my_plugin')
+ finish
+endif
+let g:loaded_my_plugin = v:true
+```
+
+### Default values
+
+```vim
+" Get a default value: if the user defines a variable use it, otherwise use a
+" hard-coded default. Uses the fact that a scope is also a dictionary.
+let s:greeting = get(g:, 'my_plugin_greeting', 'Hello')
+```