2450 lines
81 KiB
Text
2450 lines
81 KiB
Text
|
*usr_41.txt* For Vim version 7.4. Last change: 2013 Feb 20
|
||
|
|
||
|
VIM USER MANUAL - by Bram Moolenaar
|
||
|
|
||
|
Write a Vim script
|
||
|
|
||
|
|
||
|
The Vim script language is used for the startup vimrc file, syntax files, and
|
||
|
many other things. This chapter explains the items that can be used in a Vim
|
||
|
script. There are a lot of them, thus this is a long chapter.
|
||
|
|
||
|
|41.1| Introduction
|
||
|
|41.2| Variables
|
||
|
|41.3| Expressions
|
||
|
|41.4| Conditionals
|
||
|
|41.5| Executing an expression
|
||
|
|41.6| Using functions
|
||
|
|41.7| Defining a function
|
||
|
|41.8| Lists and Dictionaries
|
||
|
|41.9| Exceptions
|
||
|
|41.10| Various remarks
|
||
|
|41.11| Writing a plugin
|
||
|
|41.12| Writing a filetype plugin
|
||
|
|41.13| Writing a compiler plugin
|
||
|
|41.14| Writing a plugin that loads quickly
|
||
|
|41.15| Writing library scripts
|
||
|
|41.16| Distributing Vim scripts
|
||
|
|
||
|
Next chapter: |usr_42.txt| Add new menus
|
||
|
Previous chapter: |usr_40.txt| Make new commands
|
||
|
Table of contents: |usr_toc.txt|
|
||
|
|
||
|
==============================================================================
|
||
|
*41.1* Introduction *vim-script-intro* *script*
|
||
|
|
||
|
Your first experience with Vim scripts is the vimrc file. Vim reads it when
|
||
|
it starts up and executes the commands. You can set options to values you
|
||
|
prefer. And you can use any colon command in it (commands that start with a
|
||
|
":"; these are sometimes referred to as Ex commands or command-line commands).
|
||
|
Syntax files are also Vim scripts. As are files that set options for a
|
||
|
specific file type. A complicated macro can be defined by a separate Vim
|
||
|
script file. You can think of other uses yourself.
|
||
|
|
||
|
Let's start with a simple example: >
|
||
|
|
||
|
:let i = 1
|
||
|
:while i < 5
|
||
|
: echo "count is" i
|
||
|
: let i += 1
|
||
|
:endwhile
|
||
|
<
|
||
|
Note:
|
||
|
The ":" characters are not really needed here. You only need to use
|
||
|
them when you type a command. In a Vim script file they can be left
|
||
|
out. We will use them here anyway to make clear these are colon
|
||
|
commands and make them stand out from Normal mode commands.
|
||
|
Note:
|
||
|
You can try out the examples by yanking the lines from the text here
|
||
|
and executing them with :@"
|
||
|
|
||
|
The output of the example code is:
|
||
|
|
||
|
count is 1 ~
|
||
|
count is 2 ~
|
||
|
count is 3 ~
|
||
|
count is 4 ~
|
||
|
|
||
|
In the first line the ":let" command assigns a value to a variable. The
|
||
|
generic form is: >
|
||
|
|
||
|
:let {variable} = {expression}
|
||
|
|
||
|
In this case the variable name is "i" and the expression is a simple value,
|
||
|
the number one.
|
||
|
The ":while" command starts a loop. The generic form is: >
|
||
|
|
||
|
:while {condition}
|
||
|
: {statements}
|
||
|
:endwhile
|
||
|
|
||
|
The statements until the matching ":endwhile" are executed for as long as the
|
||
|
condition is true. The condition used here is the expression "i < 5". This
|
||
|
is true when the variable i is smaller than five.
|
||
|
Note:
|
||
|
If you happen to write a while loop that keeps on running, you can
|
||
|
interrupt it by pressing CTRL-C (CTRL-Break on MS-Windows).
|
||
|
|
||
|
The ":echo" command prints its arguments. In this case the string "count is"
|
||
|
and the value of the variable i. Since i is one, this will print:
|
||
|
|
||
|
count is 1 ~
|
||
|
|
||
|
Then there is the ":let i += 1" command. This does the same thing as
|
||
|
":let i = i + 1". This adds one to the variable i and assigns the new value
|
||
|
to the same variable.
|
||
|
|
||
|
The example was given to explain the commands, but would you really want to
|
||
|
make such a loop it can be written much more compact: >
|
||
|
|
||
|
:for i in range(1, 4)
|
||
|
: echo "count is" i
|
||
|
:endfor
|
||
|
|
||
|
We won't explain how |:for| and |range()| work until later. Follow the links
|
||
|
if you are impatient.
|
||
|
|
||
|
|
||
|
THREE KINDS OF NUMBERS
|
||
|
|
||
|
Numbers can be decimal, hexadecimal or octal. A hexadecimal number starts
|
||
|
with "0x" or "0X". For example "0x1f" is decimal 31. An octal number starts
|
||
|
with a zero. "017" is decimal 15. Careful: don't put a zero before a decimal
|
||
|
number, it will be interpreted as an octal number!
|
||
|
The ":echo" command always prints decimal numbers. Example: >
|
||
|
|
||
|
:echo 0x7f 036
|
||
|
< 127 30 ~
|
||
|
|
||
|
A number is made negative with a minus sign. This also works for hexadecimal
|
||
|
and octal numbers. A minus sign is also used for subtraction. Compare this
|
||
|
with the previous example: >
|
||
|
|
||
|
:echo 0x7f -036
|
||
|
< 97 ~
|
||
|
|
||
|
White space in an expression is ignored. However, it's recommended to use it
|
||
|
for separating items, to make the expression easier to read. For example, to
|
||
|
avoid the confusion with a negative number above, put a space between the
|
||
|
minus sign and the following number: >
|
||
|
|
||
|
:echo 0x7f - 036
|
||
|
|
||
|
==============================================================================
|
||
|
*41.2* Variables
|
||
|
|
||
|
A variable name consists of ASCII letters, digits and the underscore. It
|
||
|
cannot start with a digit. Valid variable names are:
|
||
|
|
||
|
counter
|
||
|
_aap3
|
||
|
very_long_variable_name_with_underscores
|
||
|
FuncLength
|
||
|
LENGTH
|
||
|
|
||
|
Invalid names are "foo+bar" and "6var".
|
||
|
These variables are global. To see a list of currently defined variables
|
||
|
use this command: >
|
||
|
|
||
|
:let
|
||
|
|
||
|
You can use global variables everywhere. This also means that when the
|
||
|
variable "count" is used in one script file, it might also be used in another
|
||
|
file. This leads to confusion at least, and real problems at worst. To avoid
|
||
|
this, you can use a variable local to a script file by prepending "s:". For
|
||
|
example, one script contains this code: >
|
||
|
|
||
|
:let s:count = 1
|
||
|
:while s:count < 5
|
||
|
: source other.vim
|
||
|
: let s:count += 1
|
||
|
:endwhile
|
||
|
|
||
|
Since "s:count" is local to this script, you can be sure that sourcing the
|
||
|
"other.vim" script will not change this variable. If "other.vim" also uses an
|
||
|
"s:count" variable, it will be a different copy, local to that script. More
|
||
|
about script-local variables here: |script-variable|.
|
||
|
|
||
|
There are more kinds of variables, see |internal-variables|. The most often
|
||
|
used ones are:
|
||
|
|
||
|
b:name variable local to a buffer
|
||
|
w:name variable local to a window
|
||
|
g:name global variable (also in a function)
|
||
|
v:name variable predefined by Vim
|
||
|
|
||
|
|
||
|
DELETING VARIABLES
|
||
|
|
||
|
Variables take up memory and show up in the output of the ":let" command. To
|
||
|
delete a variable use the ":unlet" command. Example: >
|
||
|
|
||
|
:unlet s:count
|
||
|
|
||
|
This deletes the script-local variable "s:count" to free up the memory it
|
||
|
uses. If you are not sure if the variable exists, and don't want an error
|
||
|
message when it doesn't, append !: >
|
||
|
|
||
|
:unlet! s:count
|
||
|
|
||
|
When a script finishes, the local variables used there will not be
|
||
|
automatically freed. The next time the script executes, it can still use the
|
||
|
old value. Example: >
|
||
|
|
||
|
:if !exists("s:call_count")
|
||
|
: let s:call_count = 0
|
||
|
:endif
|
||
|
:let s:call_count = s:call_count + 1
|
||
|
:echo "called" s:call_count "times"
|
||
|
|
||
|
The "exists()" function checks if a variable has already been defined. Its
|
||
|
argument is the name of the variable you want to check. Not the variable
|
||
|
itself! If you would do this: >
|
||
|
|
||
|
:if !exists(s:call_count)
|
||
|
|
||
|
Then the value of s:call_count will be used as the name of the variable that
|
||
|
exists() checks. That's not what you want.
|
||
|
The exclamation mark ! negates a value. When the value was true, it
|
||
|
becomes false. When it was false, it becomes true. You can read it as "not".
|
||
|
Thus "if !exists()" can be read as "if not exists()".
|
||
|
What Vim calls true is anything that is not zero. Zero is false.
|
||
|
Note:
|
||
|
Vim automatically converts a string to a number when it is looking for
|
||
|
a number. When using a string that doesn't start with a digit the
|
||
|
resulting number is zero. Thus look out for this: >
|
||
|
:if "true"
|
||
|
< The "true" will be interpreted as a zero, thus as false!
|
||
|
|
||
|
|
||
|
STRING VARIABLES AND CONSTANTS
|
||
|
|
||
|
So far only numbers were used for the variable value. Strings can be used as
|
||
|
well. Numbers and strings are the basic types of variables that Vim supports.
|
||
|
The type is dynamic, it is set each time when assigning a value to the
|
||
|
variable with ":let". More about types in |41.8|.
|
||
|
To assign a string value to a variable, you need to use a string constant.
|
||
|
There are two types of these. First the string in double quotes: >
|
||
|
|
||
|
:let name = "peter"
|
||
|
:echo name
|
||
|
< peter ~
|
||
|
|
||
|
If you want to include a double quote inside the string, put a backslash in
|
||
|
front of it: >
|
||
|
|
||
|
:let name = "\"peter\""
|
||
|
:echo name
|
||
|
< "peter" ~
|
||
|
|
||
|
To avoid the need for a backslash, you can use a string in single quotes: >
|
||
|
|
||
|
:let name = '"peter"'
|
||
|
:echo name
|
||
|
< "peter" ~
|
||
|
|
||
|
Inside a single-quote string all the characters are as they are. Only the
|
||
|
single quote itself is special: you need to use two to get one. A backslash
|
||
|
is taken literally, thus you can't use it to change the meaning of the
|
||
|
character after it.
|
||
|
In double-quote strings it is possible to use special characters. Here are
|
||
|
a few useful ones:
|
||
|
|
||
|
\t <Tab>
|
||
|
\n <NL>, line break
|
||
|
\r <CR>, <Enter>
|
||
|
\e <Esc>
|
||
|
\b <BS>, backspace
|
||
|
\" "
|
||
|
\\ \, backslash
|
||
|
\<Esc> <Esc>
|
||
|
\<C-W> CTRL-W
|
||
|
|
||
|
The last two are just examples. The "\<name>" form can be used to include
|
||
|
the special key "name".
|
||
|
See |expr-quote| for the full list of special items in a string.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.3* Expressions
|
||
|
|
||
|
Vim has a rich, yet simple way to handle expressions. You can read the
|
||
|
definition here: |expression-syntax|. Here we will show the most common
|
||
|
items.
|
||
|
The numbers, strings and variables mentioned above are expressions by
|
||
|
themselves. Thus everywhere an expression is expected, you can use a number,
|
||
|
string or variable. Other basic items in an expression are:
|
||
|
|
||
|
$NAME environment variable
|
||
|
&name option
|
||
|
@r register
|
||
|
|
||
|
Examples: >
|
||
|
|
||
|
:echo "The value of 'tabstop' is" &ts
|
||
|
:echo "Your home directory is" $HOME
|
||
|
:if @a > 5
|
||
|
|
||
|
The &name form can be used to save an option value, set it to a new value,
|
||
|
do something and restore the old value. Example: >
|
||
|
|
||
|
:let save_ic = &ic
|
||
|
:set noic
|
||
|
:/The Start/,$delete
|
||
|
:let &ic = save_ic
|
||
|
|
||
|
This makes sure the "The Start" pattern is used with the 'ignorecase' option
|
||
|
off. Still, it keeps the value that the user had set. (Another way to do
|
||
|
this would be to add "\C" to the pattern, see |/\C|.)
|
||
|
|
||
|
|
||
|
MATHEMATICS
|
||
|
|
||
|
It becomes more interesting if we combine these basic items. Let's start with
|
||
|
mathematics on numbers:
|
||
|
|
||
|
a + b add
|
||
|
a - b subtract
|
||
|
a * b multiply
|
||
|
a / b divide
|
||
|
a % b modulo
|
||
|
|
||
|
The usual precedence is used. Example: >
|
||
|
|
||
|
:echo 10 + 5 * 2
|
||
|
< 20 ~
|
||
|
|
||
|
Grouping is done with parentheses. No surprises here. Example: >
|
||
|
|
||
|
:echo (10 + 5) * 2
|
||
|
< 30 ~
|
||
|
|
||
|
Strings can be concatenated with ".". Example: >
|
||
|
|
||
|
:echo "foo" . "bar"
|
||
|
< foobar ~
|
||
|
|
||
|
When the ":echo" command gets multiple arguments, it separates them with a
|
||
|
space. In the example the argument is a single expression, thus no space is
|
||
|
inserted.
|
||
|
|
||
|
Borrowed from the C language is the conditional expression:
|
||
|
|
||
|
a ? b : c
|
||
|
|
||
|
If "a" evaluates to true "b" is used, otherwise "c" is used. Example: >
|
||
|
|
||
|
:let i = 4
|
||
|
:echo i > 5 ? "i is big" : "i is small"
|
||
|
< i is small ~
|
||
|
|
||
|
The three parts of the constructs are always evaluated first, thus you could
|
||
|
see it work as:
|
||
|
|
||
|
(a) ? (b) : (c)
|
||
|
|
||
|
==============================================================================
|
||
|
*41.4* Conditionals
|
||
|
|
||
|
The ":if" commands executes the following statements, until the matching
|
||
|
":endif", only when a condition is met. The generic form is:
|
||
|
|
||
|
:if {condition}
|
||
|
{statements}
|
||
|
:endif
|
||
|
|
||
|
Only when the expression {condition} evaluates to true (non-zero) will the
|
||
|
{statements} be executed. These must still be valid commands. If they
|
||
|
contain garbage, Vim won't be able to find the ":endif".
|
||
|
You can also use ":else". The generic form for this is:
|
||
|
|
||
|
:if {condition}
|
||
|
{statements}
|
||
|
:else
|
||
|
{statements}
|
||
|
:endif
|
||
|
|
||
|
The second {statements} is only executed if the first one isn't.
|
||
|
Finally, there is ":elseif":
|
||
|
|
||
|
:if {condition}
|
||
|
{statements}
|
||
|
:elseif {condition}
|
||
|
{statements}
|
||
|
:endif
|
||
|
|
||
|
This works just like using ":else" and then "if", but without the need for an
|
||
|
extra ":endif".
|
||
|
A useful example for your vimrc file is checking the 'term' option and
|
||
|
doing something depending upon its value: >
|
||
|
|
||
|
:if &term == "xterm"
|
||
|
: " Do stuff for xterm
|
||
|
:elseif &term == "vt100"
|
||
|
: " Do stuff for a vt100 terminal
|
||
|
:else
|
||
|
: " Do something for other terminals
|
||
|
:endif
|
||
|
|
||
|
|
||
|
LOGIC OPERATIONS
|
||
|
|
||
|
We already used some of them in the examples. These are the most often used
|
||
|
ones:
|
||
|
|
||
|
a == b equal to
|
||
|
a != b not equal to
|
||
|
a > b greater than
|
||
|
a >= b greater than or equal to
|
||
|
a < b less than
|
||
|
a <= b less than or equal to
|
||
|
|
||
|
The result is one if the condition is met and zero otherwise. An example: >
|
||
|
|
||
|
:if v:version >= 700
|
||
|
: echo "congratulations"
|
||
|
:else
|
||
|
: echo "you are using an old version, upgrade!"
|
||
|
:endif
|
||
|
|
||
|
Here "v:version" is a variable defined by Vim, which has the value of the Vim
|
||
|
version. 600 is for version 6.0. Version 6.1 has the value 601. This is
|
||
|
very useful to write a script that works with multiple versions of Vim.
|
||
|
|v:version|
|
||
|
|
||
|
The logic operators work both for numbers and strings. When comparing two
|
||
|
strings, the mathematical difference is used. This compares byte values,
|
||
|
which may not be right for some languages.
|
||
|
When comparing a string with a number, the string is first converted to a
|
||
|
number. This is a bit tricky, because when a string doesn't look like a
|
||
|
number, the number zero is used. Example: >
|
||
|
|
||
|
:if 0 == "one"
|
||
|
: echo "yes"
|
||
|
:endif
|
||
|
|
||
|
This will echo "yes", because "one" doesn't look like a number, thus it is
|
||
|
converted to the number zero.
|
||
|
|
||
|
For strings there are two more items:
|
||
|
|
||
|
a =~ b matches with
|
||
|
a !~ b does not match with
|
||
|
|
||
|
The left item "a" is used as a string. The right item "b" is used as a
|
||
|
pattern, like what's used for searching. Example: >
|
||
|
|
||
|
:if str =~ " "
|
||
|
: echo "str contains a space"
|
||
|
:endif
|
||
|
:if str !~ '\.$'
|
||
|
: echo "str does not end in a full stop"
|
||
|
:endif
|
||
|
|
||
|
Notice the use of a single-quote string for the pattern. This is useful,
|
||
|
because backslashes would need to be doubled in a double-quote string and
|
||
|
patterns tend to contain many backslashes.
|
||
|
|
||
|
The 'ignorecase' option is used when comparing strings. When you don't want
|
||
|
that, append "#" to match case and "?" to ignore case. Thus "==?" compares
|
||
|
two strings to be equal while ignoring case. And "!~#" checks if a pattern
|
||
|
doesn't match, also checking the case of letters. For the full table see
|
||
|
|expr-==|.
|
||
|
|
||
|
|
||
|
MORE LOOPING
|
||
|
|
||
|
The ":while" command was already mentioned. Two more statements can be used
|
||
|
in between the ":while" and the ":endwhile":
|
||
|
|
||
|
:continue Jump back to the start of the while loop; the
|
||
|
loop continues.
|
||
|
:break Jump forward to the ":endwhile"; the loop is
|
||
|
discontinued.
|
||
|
|
||
|
Example: >
|
||
|
|
||
|
:while counter < 40
|
||
|
: call do_something()
|
||
|
: if skip_flag
|
||
|
: continue
|
||
|
: endif
|
||
|
: if finished_flag
|
||
|
: break
|
||
|
: endif
|
||
|
: sleep 50m
|
||
|
:endwhile
|
||
|
|
||
|
The ":sleep" command makes Vim take a nap. The "50m" specifies fifty
|
||
|
milliseconds. Another example is ":sleep 4", which sleeps for four seconds.
|
||
|
|
||
|
Even more looping can be done with the ":for" command, see below in |41.8|.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.5* Executing an expression
|
||
|
|
||
|
So far the commands in the script were executed by Vim directly. The
|
||
|
":execute" command allows executing the result of an expression. This is a
|
||
|
very powerful way to build commands and execute them.
|
||
|
An example is to jump to a tag, which is contained in a variable: >
|
||
|
|
||
|
:execute "tag " . tag_name
|
||
|
|
||
|
The "." is used to concatenate the string "tag " with the value of variable
|
||
|
"tag_name". Suppose "tag_name" has the value "get_cmd", then the command that
|
||
|
will be executed is: >
|
||
|
|
||
|
:tag get_cmd
|
||
|
|
||
|
The ":execute" command can only execute colon commands. The ":normal" command
|
||
|
executes Normal mode commands. However, its argument is not an expression but
|
||
|
the literal command characters. Example: >
|
||
|
|
||
|
:normal gg=G
|
||
|
|
||
|
This jumps to the first line and formats all lines with the "=" operator.
|
||
|
To make ":normal" work with an expression, combine ":execute" with it.
|
||
|
Example: >
|
||
|
|
||
|
:execute "normal " . normal_commands
|
||
|
|
||
|
The variable "normal_commands" must contain the Normal mode commands.
|
||
|
Make sure that the argument for ":normal" is a complete command. Otherwise
|
||
|
Vim will run into the end of the argument and abort the command. For example,
|
||
|
if you start Insert mode, you must leave Insert mode as well. This works: >
|
||
|
|
||
|
:execute "normal Inew text \<Esc>"
|
||
|
|
||
|
This inserts "new text " in the current line. Notice the use of the special
|
||
|
key "\<Esc>". This avoids having to enter a real <Esc> character in your
|
||
|
script.
|
||
|
|
||
|
If you don't want to execute a string but evaluate it to get its expression
|
||
|
value, you can use the eval() function: >
|
||
|
|
||
|
:let optname = "path"
|
||
|
:let optval = eval('&' . optname)
|
||
|
|
||
|
A "&" character is prepended to "path", thus the argument to eval() is
|
||
|
"&path". The result will then be the value of the 'path' option.
|
||
|
The same thing can be done with: >
|
||
|
:exe 'let optval = &' . optname
|
||
|
|
||
|
==============================================================================
|
||
|
*41.6* Using functions
|
||
|
|
||
|
Vim defines many functions and provides a large amount of functionality that
|
||
|
way. A few examples will be given in this section. You can find the whole
|
||
|
list here: |functions|.
|
||
|
|
||
|
A function is called with the ":call" command. The parameters are passed in
|
||
|
between parentheses separated by commas. Example: >
|
||
|
|
||
|
:call search("Date: ", "W")
|
||
|
|
||
|
This calls the search() function, with arguments "Date: " and "W". The
|
||
|
search() function uses its first argument as a search pattern and the second
|
||
|
one as flags. The "W" flag means the search doesn't wrap around the end of
|
||
|
the file.
|
||
|
|
||
|
A function can be called in an expression. Example: >
|
||
|
|
||
|
:let line = getline(".")
|
||
|
:let repl = substitute(line, '\a', "*", "g")
|
||
|
:call setline(".", repl)
|
||
|
|
||
|
The getline() function obtains a line from the current buffer. Its argument
|
||
|
is a specification of the line number. In this case "." is used, which means
|
||
|
the line where the cursor is.
|
||
|
The substitute() function does something similar to the ":substitute"
|
||
|
command. The first argument is the string on which to perform the
|
||
|
substitution. The second argument is the pattern, the third the replacement
|
||
|
string. Finally, the last arguments are the flags.
|
||
|
The setline() function sets the line, specified by the first argument, to a
|
||
|
new string, the second argument. In this example the line under the cursor is
|
||
|
replaced with the result of the substitute(). Thus the effect of the three
|
||
|
statements is equal to: >
|
||
|
|
||
|
:substitute/\a/*/g
|
||
|
|
||
|
Using the functions becomes more interesting when you do more work before and
|
||
|
after the substitute() call.
|
||
|
|
||
|
|
||
|
FUNCTIONS *function-list*
|
||
|
|
||
|
There are many functions. We will mention them here, grouped by what they are
|
||
|
used for. You can find an alphabetical list here: |functions|. Use CTRL-] on
|
||
|
the function name to jump to detailed help on it.
|
||
|
|
||
|
String manipulation: *string-functions*
|
||
|
nr2char() get a character by its ASCII value
|
||
|
char2nr() get ASCII value of a character
|
||
|
str2nr() convert a string to a Number
|
||
|
str2float() convert a string to a Float
|
||
|
printf() format a string according to % items
|
||
|
escape() escape characters in a string with a '\'
|
||
|
shellescape() escape a string for use with a shell command
|
||
|
fnameescape() escape a file name for use with a Vim command
|
||
|
tr() translate characters from one set to another
|
||
|
strtrans() translate a string to make it printable
|
||
|
tolower() turn a string to lowercase
|
||
|
toupper() turn a string to uppercase
|
||
|
match() position where a pattern matches in a string
|
||
|
matchend() position where a pattern match ends in a string
|
||
|
matchstr() match of a pattern in a string
|
||
|
matchlist() like matchstr() and also return submatches
|
||
|
stridx() first index of a short string in a long string
|
||
|
strridx() last index of a short string in a long string
|
||
|
strlen() length of a string
|
||
|
substitute() substitute a pattern match with a string
|
||
|
submatch() get a specific match in ":s" and substitute()
|
||
|
strpart() get part of a string
|
||
|
expand() expand special keywords
|
||
|
iconv() convert text from one encoding to another
|
||
|
byteidx() byte index of a character in a string
|
||
|
repeat() repeat a string multiple times
|
||
|
eval() evaluate a string expression
|
||
|
|
||
|
List manipulation: *list-functions*
|
||
|
get() get an item without error for wrong index
|
||
|
len() number of items in a List
|
||
|
empty() check if List is empty
|
||
|
insert() insert an item somewhere in a List
|
||
|
add() append an item to a List
|
||
|
extend() append a List to a List
|
||
|
remove() remove one or more items from a List
|
||
|
copy() make a shallow copy of a List
|
||
|
deepcopy() make a full copy of a List
|
||
|
filter() remove selected items from a List
|
||
|
map() change each List item
|
||
|
sort() sort a List
|
||
|
reverse() reverse the order of a List
|
||
|
split() split a String into a List
|
||
|
join() join List items into a String
|
||
|
range() return a List with a sequence of numbers
|
||
|
string() String representation of a List
|
||
|
call() call a function with List as arguments
|
||
|
index() index of a value in a List
|
||
|
max() maximum value in a List
|
||
|
min() minimum value in a List
|
||
|
count() count number of times a value appears in a List
|
||
|
repeat() repeat a List multiple times
|
||
|
|
||
|
Dictionary manipulation: *dict-functions*
|
||
|
get() get an entry without an error for a wrong key
|
||
|
len() number of entries in a Dictionary
|
||
|
has_key() check whether a key appears in a Dictionary
|
||
|
empty() check if Dictionary is empty
|
||
|
remove() remove an entry from a Dictionary
|
||
|
extend() add entries from one Dictionary to another
|
||
|
filter() remove selected entries from a Dictionary
|
||
|
map() change each Dictionary entry
|
||
|
keys() get List of Dictionary keys
|
||
|
values() get List of Dictionary values
|
||
|
items() get List of Dictionary key-value pairs
|
||
|
copy() make a shallow copy of a Dictionary
|
||
|
deepcopy() make a full copy of a Dictionary
|
||
|
string() String representation of a Dictionary
|
||
|
max() maximum value in a Dictionary
|
||
|
min() minimum value in a Dictionary
|
||
|
count() count number of times a value appears
|
||
|
|
||
|
Floating point computation: *float-functions*
|
||
|
float2nr() convert Float to Number
|
||
|
abs() absolute value (also works for Number)
|
||
|
round() round off
|
||
|
ceil() round up
|
||
|
floor() round down
|
||
|
trunc() remove value after decimal point
|
||
|
log10() logarithm to base 10
|
||
|
pow() value of x to the exponent y
|
||
|
sqrt() square root
|
||
|
sin() sine
|
||
|
cos() cosine
|
||
|
tan() tangent
|
||
|
asin() arc sine
|
||
|
acos() arc cosine
|
||
|
atan() arc tangent
|
||
|
atan2() arc tangent
|
||
|
sinh() hyperbolic sine
|
||
|
cosh() hyperbolic cosine
|
||
|
tanh() hyperbolic tangent
|
||
|
|
||
|
Other computation: *bitwise-function*
|
||
|
and() bitwise AND
|
||
|
invert() bitwise invert
|
||
|
or() bitwise OR
|
||
|
xor() bitwise XOR
|
||
|
|
||
|
Variables: *var-functions*
|
||
|
type() type of a variable
|
||
|
islocked() check if a variable is locked
|
||
|
function() get a Funcref for a function name
|
||
|
getbufvar() get a variable value from a specific buffer
|
||
|
setbufvar() set a variable in a specific buffer
|
||
|
getwinvar() get a variable from specific window
|
||
|
gettabvar() get a variable from specific tab page
|
||
|
gettabwinvar() get a variable from specific window & tab page
|
||
|
setwinvar() set a variable in a specific window
|
||
|
settabvar() set a variable in a specific tab page
|
||
|
settabwinvar() set a variable in a specific window & tab page
|
||
|
garbagecollect() possibly free memory
|
||
|
|
||
|
Cursor and mark position: *cursor-functions* *mark-functions*
|
||
|
col() column number of the cursor or a mark
|
||
|
virtcol() screen column of the cursor or a mark
|
||
|
line() line number of the cursor or mark
|
||
|
wincol() window column number of the cursor
|
||
|
winline() window line number of the cursor
|
||
|
cursor() position the cursor at a line/column
|
||
|
getpos() get position of cursor, mark, etc.
|
||
|
setpos() set position of cursor, mark, etc.
|
||
|
byte2line() get line number at a specific byte count
|
||
|
line2byte() byte count at a specific line
|
||
|
diff_filler() get the number of filler lines above a line
|
||
|
|
||
|
Working with text in the current buffer: *text-functions*
|
||
|
getline() get a line or list of lines from the buffer
|
||
|
setline() replace a line in the buffer
|
||
|
append() append line or list of lines in the buffer
|
||
|
indent() indent of a specific line
|
||
|
cindent() indent according to C indenting
|
||
|
lispindent() indent according to Lisp indenting
|
||
|
nextnonblank() find next non-blank line
|
||
|
prevnonblank() find previous non-blank line
|
||
|
search() find a match for a pattern
|
||
|
searchpos() find a match for a pattern
|
||
|
searchpair() find the other end of a start/skip/end
|
||
|
searchpairpos() find the other end of a start/skip/end
|
||
|
searchdecl() search for the declaration of a name
|
||
|
|
||
|
*system-functions* *file-functions*
|
||
|
System functions and manipulation of files:
|
||
|
glob() expand wildcards
|
||
|
globpath() expand wildcards in a number of directories
|
||
|
findfile() find a file in a list of directories
|
||
|
finddir() find a directory in a list of directories
|
||
|
resolve() find out where a shortcut points to
|
||
|
fnamemodify() modify a file name
|
||
|
pathshorten() shorten directory names in a path
|
||
|
simplify() simplify a path without changing its meaning
|
||
|
executable() check if an executable program exists
|
||
|
filereadable() check if a file can be read
|
||
|
filewritable() check if a file can be written to
|
||
|
getfperm() get the permissions of a file
|
||
|
getftype() get the kind of a file
|
||
|
isdirectory() check if a directory exists
|
||
|
getfsize() get the size of a file
|
||
|
getcwd() get the current working directory
|
||
|
haslocaldir() check if current window used |:lcd|
|
||
|
tempname() get the name of a temporary file
|
||
|
mkdir() create a new directory
|
||
|
delete() delete a file
|
||
|
rename() rename a file
|
||
|
system() get the result of a shell command
|
||
|
hostname() name of the system
|
||
|
readfile() read a file into a List of lines
|
||
|
writefile() write a List of lines into a file
|
||
|
|
||
|
Date and Time: *date-functions* *time-functions*
|
||
|
getftime() get last modification time of a file
|
||
|
localtime() get current time in seconds
|
||
|
strftime() convert time to a string
|
||
|
reltime() get the current or elapsed time accurately
|
||
|
reltimestr() convert reltime() result to a string
|
||
|
|
||
|
*buffer-functions* *window-functions* *arg-functions*
|
||
|
Buffers, windows and the argument list:
|
||
|
argc() number of entries in the argument list
|
||
|
argidx() current position in the argument list
|
||
|
argv() get one entry from the argument list
|
||
|
bufexists() check if a buffer exists
|
||
|
buflisted() check if a buffer exists and is listed
|
||
|
bufloaded() check if a buffer exists and is loaded
|
||
|
bufname() get the name of a specific buffer
|
||
|
bufnr() get the buffer number of a specific buffer
|
||
|
tabpagebuflist() return List of buffers in a tab page
|
||
|
tabpagenr() get the number of a tab page
|
||
|
tabpagewinnr() like winnr() for a specified tab page
|
||
|
winnr() get the window number for the current window
|
||
|
bufwinnr() get the window number of a specific buffer
|
||
|
winbufnr() get the buffer number of a specific window
|
||
|
getbufline() get a list of lines from the specified buffer
|
||
|
|
||
|
Command line: *command-line-functions*
|
||
|
getcmdline() get the current command line
|
||
|
getcmdpos() get position of the cursor in the command line
|
||
|
setcmdpos() set position of the cursor in the command line
|
||
|
getcmdtype() return the current command-line type
|
||
|
|
||
|
Quickfix and location lists: *quickfix-functions*
|
||
|
getqflist() list of quickfix errors
|
||
|
setqflist() modify a quickfix list
|
||
|
getloclist() list of location list items
|
||
|
setloclist() modify a location list
|
||
|
|
||
|
Insert mode completion: *completion-functions*
|
||
|
complete() set found matches
|
||
|
complete_add() add to found matches
|
||
|
complete_check() check if completion should be aborted
|
||
|
pumvisible() check if the popup menu is displayed
|
||
|
|
||
|
Folding: *folding-functions*
|
||
|
foldclosed() check for a closed fold at a specific line
|
||
|
foldclosedend() like foldclosed() but return the last line
|
||
|
foldlevel() check for the fold level at a specific line
|
||
|
foldtext() generate the line displayed for a closed fold
|
||
|
foldtextresult() get the text displayed for a closed fold
|
||
|
|
||
|
Syntax and highlighting: *syntax-functions* *highlighting-functions*
|
||
|
clearmatches() clear all matches defined by |matchadd()| and
|
||
|
the |:match| commands
|
||
|
getmatches() get all matches defined by |matchadd()| and
|
||
|
the |:match| commands
|
||
|
hlexists() check if a highlight group exists
|
||
|
hlID() get ID of a highlight group
|
||
|
synID() get syntax ID at a specific position
|
||
|
synIDattr() get a specific attribute of a syntax ID
|
||
|
synIDtrans() get translated syntax ID
|
||
|
synstack() get list of syntax IDs at a specific position
|
||
|
synconcealed() get info about concealing
|
||
|
diff_hlID() get highlight ID for diff mode at a position
|
||
|
matchadd() define a pattern to highlight (a "match")
|
||
|
matcharg() get info about |:match| arguments
|
||
|
matchdelete() delete a match defined by |matchadd()| or a
|
||
|
|:match| command
|
||
|
setmatches() restore a list of matches saved by
|
||
|
|getmatches()|
|
||
|
|
||
|
Spelling: *spell-functions*
|
||
|
spellbadword() locate badly spelled word at or after cursor
|
||
|
spellsuggest() return suggested spelling corrections
|
||
|
soundfold() return the sound-a-like equivalent of a word
|
||
|
|
||
|
History: *history-functions*
|
||
|
histadd() add an item to a history
|
||
|
histdel() delete an item from a history
|
||
|
histget() get an item from a history
|
||
|
histnr() get highest index of a history list
|
||
|
|
||
|
Interactive: *interactive-functions*
|
||
|
browse() put up a file requester
|
||
|
browsedir() put up a directory requester
|
||
|
confirm() let the user make a choice
|
||
|
getchar() get a character from the user
|
||
|
getcharmod() get modifiers for the last typed character
|
||
|
feedkeys() put characters in the typeahead queue
|
||
|
input() get a line from the user
|
||
|
inputlist() let the user pick an entry from a list
|
||
|
inputsecret() get a line from the user without showing it
|
||
|
inputdialog() get a line from the user in a dialog
|
||
|
inputsave() save and clear typeahead
|
||
|
inputrestore() restore typeahead
|
||
|
|
||
|
GUI: *gui-functions*
|
||
|
getfontname() get name of current font being used
|
||
|
getwinposx() X position of the GUI Vim window
|
||
|
getwinposy() Y position of the GUI Vim window
|
||
|
|
||
|
Vim server: *server-functions*
|
||
|
serverlist() return the list of server names
|
||
|
remote_send() send command characters to a Vim server
|
||
|
remote_expr() evaluate an expression in a Vim server
|
||
|
server2client() send a reply to a client of a Vim server
|
||
|
remote_peek() check if there is a reply from a Vim server
|
||
|
remote_read() read a reply from a Vim server
|
||
|
foreground() move the Vim window to the foreground
|
||
|
remote_foreground() move the Vim server window to the foreground
|
||
|
|
||
|
Window size and position: *window-size-functions*
|
||
|
winheight() get height of a specific window
|
||
|
winwidth() get width of a specific window
|
||
|
winrestcmd() return command to restore window sizes
|
||
|
winsaveview() get view of current window
|
||
|
winrestview() restore saved view of current window
|
||
|
|
||
|
Mappings: *mapping-functions*
|
||
|
hasmapto() check if a mapping exists
|
||
|
mapcheck() check if a matching mapping exists
|
||
|
maparg() get rhs of a mapping
|
||
|
wildmenumode() check if the wildmode is active
|
||
|
|
||
|
Various: *various-functions*
|
||
|
mode() get current editing mode
|
||
|
visualmode() last visual mode used
|
||
|
exists() check if a variable, function, etc. exists
|
||
|
has() check if a feature is supported in Vim
|
||
|
changenr() return number of most recent change
|
||
|
cscope_connection() check if a cscope connection exists
|
||
|
did_filetype() check if a FileType autocommand was used
|
||
|
eventhandler() check if invoked by an event handler
|
||
|
getpid() get process ID of Vim
|
||
|
|
||
|
libcall() call a function in an external library
|
||
|
libcallnr() idem, returning a number
|
||
|
|
||
|
getreg() get contents of a register
|
||
|
getregtype() get type of a register
|
||
|
setreg() set contents and type of a register
|
||
|
|
||
|
taglist() get list of matching tags
|
||
|
tagfiles() get a list of tags files
|
||
|
|
||
|
mzeval() evaluate |MzScheme| expression
|
||
|
|
||
|
==============================================================================
|
||
|
*41.7* Defining a function
|
||
|
|
||
|
Vim enables you to define your own functions. The basic function declaration
|
||
|
begins as follows: >
|
||
|
|
||
|
:function {name}({var1}, {var2}, ...)
|
||
|
: {body}
|
||
|
:endfunction
|
||
|
<
|
||
|
Note:
|
||
|
Function names must begin with a capital letter.
|
||
|
|
||
|
Let's define a short function to return the smaller of two numbers. It starts
|
||
|
with this line: >
|
||
|
|
||
|
:function Min(num1, num2)
|
||
|
|
||
|
This tells Vim that the function is named "Min" and it takes two arguments:
|
||
|
"num1" and "num2".
|
||
|
The first thing you need to do is to check to see which number is smaller:
|
||
|
>
|
||
|
: if a:num1 < a:num2
|
||
|
|
||
|
The special prefix "a:" tells Vim that the variable is a function argument.
|
||
|
Let's assign the variable "smaller" the value of the smallest number: >
|
||
|
|
||
|
: if a:num1 < a:num2
|
||
|
: let smaller = a:num1
|
||
|
: else
|
||
|
: let smaller = a:num2
|
||
|
: endif
|
||
|
|
||
|
The variable "smaller" is a local variable. Variables used inside a function
|
||
|
are local unless prefixed by something like "g:", "a:", or "s:".
|
||
|
|
||
|
Note:
|
||
|
To access a global variable from inside a function you must prepend
|
||
|
"g:" to it. Thus "g:today" inside a function is used for the global
|
||
|
variable "today", and "today" is another variable, local to the
|
||
|
function.
|
||
|
|
||
|
You now use the ":return" statement to return the smallest number to the user.
|
||
|
Finally, you end the function: >
|
||
|
|
||
|
: return smaller
|
||
|
:endfunction
|
||
|
|
||
|
The complete function definition is as follows: >
|
||
|
|
||
|
:function Min(num1, num2)
|
||
|
: if a:num1 < a:num2
|
||
|
: let smaller = a:num1
|
||
|
: else
|
||
|
: let smaller = a:num2
|
||
|
: endif
|
||
|
: return smaller
|
||
|
:endfunction
|
||
|
|
||
|
For people who like short functions, this does the same thing: >
|
||
|
|
||
|
:function Min(num1, num2)
|
||
|
: if a:num1 < a:num2
|
||
|
: return a:num1
|
||
|
: endif
|
||
|
: return a:num2
|
||
|
:endfunction
|
||
|
|
||
|
A user defined function is called in exactly the same way as a built-in
|
||
|
function. Only the name is different. The Min function can be used like
|
||
|
this: >
|
||
|
|
||
|
:echo Min(5, 8)
|
||
|
|
||
|
Only now will the function be executed and the lines be interpreted by Vim.
|
||
|
If there are mistakes, like using an undefined variable or function, you will
|
||
|
now get an error message. When defining the function these errors are not
|
||
|
detected.
|
||
|
|
||
|
When a function reaches ":endfunction" or ":return" is used without an
|
||
|
argument, the function returns zero.
|
||
|
|
||
|
To redefine a function that already exists, use the ! for the ":function"
|
||
|
command: >
|
||
|
|
||
|
:function! Min(num1, num2, num3)
|
||
|
|
||
|
|
||
|
USING A RANGE
|
||
|
|
||
|
The ":call" command can be given a line range. This can have one of two
|
||
|
meanings. When a function has been defined with the "range" keyword, it will
|
||
|
take care of the line range itself.
|
||
|
The function will be passed the variables "a:firstline" and "a:lastline".
|
||
|
These will have the line numbers from the range the function was called with.
|
||
|
Example: >
|
||
|
|
||
|
:function Count_words() range
|
||
|
: let lnum = a:firstline
|
||
|
: let n = 0
|
||
|
: while lnum <= a:lastline
|
||
|
: let n = n + len(split(getline(lnum)))
|
||
|
: let lnum = lnum + 1
|
||
|
: endwhile
|
||
|
: echo "found " . n . " words"
|
||
|
:endfunction
|
||
|
|
||
|
You can call this function with: >
|
||
|
|
||
|
:10,30call Count_words()
|
||
|
|
||
|
It will be executed once and echo the number of words.
|
||
|
The other way to use a line range is by defining a function without the
|
||
|
"range" keyword. The function will be called once for every line in the
|
||
|
range, with the cursor in that line. Example: >
|
||
|
|
||
|
:function Number()
|
||
|
: echo "line " . line(".") . " contains: " . getline(".")
|
||
|
:endfunction
|
||
|
|
||
|
If you call this function with: >
|
||
|
|
||
|
:10,15call Number()
|
||
|
|
||
|
The function will be called six times.
|
||
|
|
||
|
|
||
|
VARIABLE NUMBER OF ARGUMENTS
|
||
|
|
||
|
Vim enables you to define functions that have a variable number of arguments.
|
||
|
The following command, for instance, defines a function that must have 1
|
||
|
argument (start) and can have up to 20 additional arguments: >
|
||
|
|
||
|
:function Show(start, ...)
|
||
|
|
||
|
The variable "a:1" contains the first optional argument, "a:2" the second, and
|
||
|
so on. The variable "a:0" contains the number of extra arguments.
|
||
|
For example: >
|
||
|
|
||
|
:function Show(start, ...)
|
||
|
: echohl Title
|
||
|
: echo "start is " . a:start
|
||
|
: echohl None
|
||
|
: let index = 1
|
||
|
: while index <= a:0
|
||
|
: echo " Arg " . index . " is " . a:{index}
|
||
|
: let index = index + 1
|
||
|
: endwhile
|
||
|
: echo ""
|
||
|
:endfunction
|
||
|
|
||
|
This uses the ":echohl" command to specify the highlighting used for the
|
||
|
following ":echo" command. ":echohl None" stops it again. The ":echon"
|
||
|
command works like ":echo", but doesn't output a line break.
|
||
|
|
||
|
You can also use the a:000 variable, it is a List of all the "..." arguments.
|
||
|
See |a:000|.
|
||
|
|
||
|
|
||
|
LISTING FUNCTIONS
|
||
|
|
||
|
The ":function" command lists the names and arguments of all user-defined
|
||
|
functions: >
|
||
|
|
||
|
:function
|
||
|
< function Show(start, ...) ~
|
||
|
function GetVimIndent() ~
|
||
|
function SetSyn(name) ~
|
||
|
|
||
|
To see what a function does, use its name as an argument for ":function": >
|
||
|
|
||
|
:function SetSyn
|
||
|
< 1 if &syntax == '' ~
|
||
|
2 let &syntax = a:name ~
|
||
|
3 endif ~
|
||
|
endfunction ~
|
||
|
|
||
|
|
||
|
DEBUGGING
|
||
|
|
||
|
The line number is useful for when you get an error message or when debugging.
|
||
|
See |debug-scripts| about debugging mode.
|
||
|
You can also set the 'verbose' option to 12 or higher to see all function
|
||
|
calls. Set it to 15 or higher to see every executed line.
|
||
|
|
||
|
|
||
|
DELETING A FUNCTION
|
||
|
|
||
|
To delete the Show() function: >
|
||
|
|
||
|
:delfunction Show
|
||
|
|
||
|
You get an error when the function doesn't exist.
|
||
|
|
||
|
|
||
|
FUNCTION REFERENCES
|
||
|
|
||
|
Sometimes it can be useful to have a variable point to one function or
|
||
|
another. You can do it with the function() function. It turns the name of a
|
||
|
function into a reference: >
|
||
|
|
||
|
:let result = 0 " or 1
|
||
|
:function! Right()
|
||
|
: return 'Right!'
|
||
|
:endfunc
|
||
|
:function! Wrong()
|
||
|
: return 'Wrong!'
|
||
|
:endfunc
|
||
|
:
|
||
|
:if result == 1
|
||
|
: let Afunc = function('Right')
|
||
|
:else
|
||
|
: let Afunc = function('Wrong')
|
||
|
:endif
|
||
|
:echo call(Afunc, [])
|
||
|
< Wrong! ~
|
||
|
|
||
|
Note that the name of a variable that holds a function reference must start
|
||
|
with a capital. Otherwise it could be confused with the name of a builtin
|
||
|
function.
|
||
|
The way to invoke a function that a variable refers to is with the call()
|
||
|
function. Its first argument is the function reference, the second argument
|
||
|
is a List with arguments.
|
||
|
|
||
|
Function references are most useful in combination with a Dictionary, as is
|
||
|
explained in the next section.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.8* Lists and Dictionaries
|
||
|
|
||
|
So far we have used the basic types String and Number. Vim also supports two
|
||
|
composite types: List and Dictionary.
|
||
|
|
||
|
A List is an ordered sequence of things. The things can be any kind of value,
|
||
|
thus you can make a List of numbers, a List of Lists and even a List of mixed
|
||
|
items. To create a List with three strings: >
|
||
|
|
||
|
:let alist = ['aap', 'mies', 'noot']
|
||
|
|
||
|
The List items are enclosed in square brackets and separated by commas. To
|
||
|
create an empty List: >
|
||
|
|
||
|
:let alist = []
|
||
|
|
||
|
You can add items to a List with the add() function: >
|
||
|
|
||
|
:let alist = []
|
||
|
:call add(alist, 'foo')
|
||
|
:call add(alist, 'bar')
|
||
|
:echo alist
|
||
|
< ['foo', 'bar'] ~
|
||
|
|
||
|
List concatenation is done with +: >
|
||
|
|
||
|
:echo alist + ['foo', 'bar']
|
||
|
< ['foo', 'bar', 'foo', 'bar'] ~
|
||
|
|
||
|
Or, if you want to extend a List directly: >
|
||
|
|
||
|
:let alist = ['one']
|
||
|
:call extend(alist, ['two', 'three'])
|
||
|
:echo alist
|
||
|
< ['one', 'two', 'three'] ~
|
||
|
|
||
|
Notice that using add() will have a different effect: >
|
||
|
|
||
|
:let alist = ['one']
|
||
|
:call add(alist, ['two', 'three'])
|
||
|
:echo alist
|
||
|
< ['one', ['two', 'three']] ~
|
||
|
|
||
|
The second argument of add() is added as a single item.
|
||
|
|
||
|
|
||
|
FOR LOOP
|
||
|
|
||
|
One of the nice things you can do with a List is iterate over it: >
|
||
|
|
||
|
:let alist = ['one', 'two', 'three']
|
||
|
:for n in alist
|
||
|
: echo n
|
||
|
:endfor
|
||
|
< one ~
|
||
|
two ~
|
||
|
three ~
|
||
|
|
||
|
This will loop over each element in List "alist", assigning the value to
|
||
|
variable "n". The generic form of a for loop is: >
|
||
|
|
||
|
:for {varname} in {listexpression}
|
||
|
: {commands}
|
||
|
:endfor
|
||
|
|
||
|
To loop a certain number of times you need a List of a specific length. The
|
||
|
range() function creates one for you: >
|
||
|
|
||
|
:for a in range(3)
|
||
|
: echo a
|
||
|
:endfor
|
||
|
< 0 ~
|
||
|
1 ~
|
||
|
2 ~
|
||
|
|
||
|
Notice that the first item of the List that range() produces is zero, thus the
|
||
|
last item is one less than the length of the list.
|
||
|
You can also specify the maximum value, the stride and even go backwards: >
|
||
|
|
||
|
:for a in range(8, 4, -2)
|
||
|
: echo a
|
||
|
:endfor
|
||
|
< 8 ~
|
||
|
6 ~
|
||
|
4 ~
|
||
|
|
||
|
A more useful example, looping over lines in the buffer: >
|
||
|
|
||
|
:for line in getline(1, 20)
|
||
|
: if line =~ "Date: "
|
||
|
: echo matchstr(line, 'Date: \zs.*')
|
||
|
: endif
|
||
|
:endfor
|
||
|
|
||
|
This looks into lines 1 to 20 (inclusive) and echoes any date found in there.
|
||
|
|
||
|
|
||
|
DICTIONARIES
|
||
|
|
||
|
A Dictionary stores key-value pairs. You can quickly lookup a value if you
|
||
|
know the key. A Dictionary is created with curly braces: >
|
||
|
|
||
|
:let uk2nl = {'one': 'een', 'two': 'twee', 'three': 'drie'}
|
||
|
|
||
|
Now you can lookup words by putting the key in square brackets: >
|
||
|
|
||
|
:echo uk2nl['two']
|
||
|
< twee ~
|
||
|
|
||
|
The generic form for defining a Dictionary is: >
|
||
|
|
||
|
{<key> : <value>, ...}
|
||
|
|
||
|
An empty Dictionary is one without any keys: >
|
||
|
|
||
|
{}
|
||
|
|
||
|
The possibilities with Dictionaries are numerous. There are various functions
|
||
|
for them as well. For example, you can obtain a list of the keys and loop
|
||
|
over them: >
|
||
|
|
||
|
:for key in keys(uk2nl)
|
||
|
: echo key
|
||
|
:endfor
|
||
|
< three ~
|
||
|
one ~
|
||
|
two ~
|
||
|
|
||
|
You will notice the keys are not ordered. You can sort the list to get a
|
||
|
specific order: >
|
||
|
|
||
|
:for key in sort(keys(uk2nl))
|
||
|
: echo key
|
||
|
:endfor
|
||
|
< one ~
|
||
|
three ~
|
||
|
two ~
|
||
|
|
||
|
But you can never get back the order in which items are defined. For that you
|
||
|
need to use a List, it stores items in an ordered sequence.
|
||
|
|
||
|
|
||
|
DICTIONARY FUNCTIONS
|
||
|
|
||
|
The items in a Dictionary can normally be obtained with an index in square
|
||
|
brackets: >
|
||
|
|
||
|
:echo uk2nl['one']
|
||
|
< een ~
|
||
|
|
||
|
A method that does the same, but without so many punctuation characters: >
|
||
|
|
||
|
:echo uk2nl.one
|
||
|
< een ~
|
||
|
|
||
|
This only works for a key that is made of ASCII letters, digits and the
|
||
|
underscore. You can also assign a new value this way: >
|
||
|
|
||
|
:let uk2nl.four = 'vier'
|
||
|
:echo uk2nl
|
||
|
< {'three': 'drie', 'four': 'vier', 'one': 'een', 'two': 'twee'} ~
|
||
|
|
||
|
And now for something special: you can directly define a function and store a
|
||
|
reference to it in the dictionary: >
|
||
|
|
||
|
:function uk2nl.translate(line) dict
|
||
|
: return join(map(split(a:line), 'get(self, v:val, "???")'))
|
||
|
:endfunction
|
||
|
|
||
|
Let's first try it out: >
|
||
|
|
||
|
:echo uk2nl.translate('three two five one')
|
||
|
< drie twee ??? een ~
|
||
|
|
||
|
The first special thing you notice is the "dict" at the end of the ":function"
|
||
|
line. This marks the function as being used from a Dictionary. The "self"
|
||
|
local variable will then refer to that Dictionary.
|
||
|
Now let's break up the complicated return command: >
|
||
|
|
||
|
split(a:line)
|
||
|
|
||
|
The split() function takes a string, chops it into whitespace separated words
|
||
|
and returns a list with these words. Thus in the example it returns: >
|
||
|
|
||
|
:echo split('three two five one')
|
||
|
< ['three', 'two', 'five', 'one'] ~
|
||
|
|
||
|
This list is the first argument to the map() function. This will go through
|
||
|
the list, evaluating its second argument with "v:val" set to the value of each
|
||
|
item. This is a shortcut to using a for loop. This command: >
|
||
|
|
||
|
:let alist = map(split(a:line), 'get(self, v:val, "???")')
|
||
|
|
||
|
Is equivalent to: >
|
||
|
|
||
|
:let alist = split(a:line)
|
||
|
:for idx in range(len(alist))
|
||
|
: let alist[idx] = get(self, alist[idx], "???")
|
||
|
:endfor
|
||
|
|
||
|
The get() function checks if a key is present in a Dictionary. If it is, then
|
||
|
the value is retrieved. If it isn't, then the default value is returned, in
|
||
|
the example it's '???'. This is a convenient way to handle situations where a
|
||
|
key may not be present and you don't want an error message.
|
||
|
|
||
|
The join() function does the opposite of split(): it joins together a list of
|
||
|
words, putting a space in between.
|
||
|
This combination of split(), map() and join() is a nice way to filter a line
|
||
|
of words in a very compact way.
|
||
|
|
||
|
|
||
|
OBJECT ORIENTED PROGRAMMING
|
||
|
|
||
|
Now that you can put both values and functions in a Dictionary, you can
|
||
|
actually use a Dictionary like an object.
|
||
|
Above we used a Dictionary for translating Dutch to English. We might want
|
||
|
to do the same for other languages. Let's first make an object (aka
|
||
|
Dictionary) that has the translate function, but no words to translate: >
|
||
|
|
||
|
:let transdict = {}
|
||
|
:function transdict.translate(line) dict
|
||
|
: return join(map(split(a:line), 'get(self.words, v:val, "???")'))
|
||
|
:endfunction
|
||
|
|
||
|
It's slightly different from the function above, using 'self.words' to lookup
|
||
|
word translations. But we don't have a self.words. Thus you could call this
|
||
|
an abstract class.
|
||
|
|
||
|
Now we can instantiate a Dutch translation object: >
|
||
|
|
||
|
:let uk2nl = copy(transdict)
|
||
|
:let uk2nl.words = {'one': 'een', 'two': 'twee', 'three': 'drie'}
|
||
|
:echo uk2nl.translate('three one')
|
||
|
< drie een ~
|
||
|
|
||
|
And a German translator: >
|
||
|
|
||
|
:let uk2de = copy(transdict)
|
||
|
:let uk2de.words = {'one': 'ein', 'two': 'zwei', 'three': 'drei'}
|
||
|
:echo uk2de.translate('three one')
|
||
|
< drei ein ~
|
||
|
|
||
|
You see that the copy() function is used to make a copy of the "transdict"
|
||
|
Dictionary and then the copy is changed to add the words. The original
|
||
|
remains the same, of course.
|
||
|
|
||
|
Now you can go one step further, and use your preferred translator: >
|
||
|
|
||
|
:if $LANG =~ "de"
|
||
|
: let trans = uk2de
|
||
|
:else
|
||
|
: let trans = uk2nl
|
||
|
:endif
|
||
|
:echo trans.translate('one two three')
|
||
|
< een twee drie ~
|
||
|
|
||
|
Here "trans" refers to one of the two objects (Dictionaries). No copy is
|
||
|
made. More about List and Dictionary identity can be found at |list-identity|
|
||
|
and |dict-identity|.
|
||
|
|
||
|
Now you might use a language that isn't supported. You can overrule the
|
||
|
translate() function to do nothing: >
|
||
|
|
||
|
:let uk2uk = copy(transdict)
|
||
|
:function! uk2uk.translate(line)
|
||
|
: return a:line
|
||
|
:endfunction
|
||
|
:echo uk2uk.translate('three one wladiwostok')
|
||
|
< three one wladiwostok ~
|
||
|
|
||
|
Notice that a ! was used to overwrite the existing function reference. Now
|
||
|
use "uk2uk" when no recognized language is found: >
|
||
|
|
||
|
:if $LANG =~ "de"
|
||
|
: let trans = uk2de
|
||
|
:elseif $LANG =~ "nl"
|
||
|
: let trans = uk2nl
|
||
|
:else
|
||
|
: let trans = uk2uk
|
||
|
:endif
|
||
|
:echo trans.translate('one two three')
|
||
|
< one two three ~
|
||
|
|
||
|
For further reading see |Lists| and |Dictionaries|.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.9* Exceptions
|
||
|
|
||
|
Let's start with an example: >
|
||
|
|
||
|
:try
|
||
|
: read ~/templates/pascal.tmpl
|
||
|
:catch /E484:/
|
||
|
: echo "Sorry, the Pascal template file cannot be found."
|
||
|
:endtry
|
||
|
|
||
|
The ":read" command will fail if the file does not exist. Instead of
|
||
|
generating an error message, this code catches the error and gives the user a
|
||
|
nice message.
|
||
|
|
||
|
For the commands in between ":try" and ":endtry" errors are turned into
|
||
|
exceptions. An exception is a string. In the case of an error the string
|
||
|
contains the error message. And every error message has a number. In this
|
||
|
case, the error we catch contains "E484:". This number is guaranteed to stay
|
||
|
the same (the text may change, e.g., it may be translated).
|
||
|
|
||
|
When the ":read" command causes another error, the pattern "E484:" will not
|
||
|
match in it. Thus this exception will not be caught and result in the usual
|
||
|
error message.
|
||
|
|
||
|
You might be tempted to do this: >
|
||
|
|
||
|
:try
|
||
|
: read ~/templates/pascal.tmpl
|
||
|
:catch
|
||
|
: echo "Sorry, the Pascal template file cannot be found."
|
||
|
:endtry
|
||
|
|
||
|
This means all errors are caught. But then you will not see errors that are
|
||
|
useful, such as "E21: Cannot make changes, 'modifiable' is off".
|
||
|
|
||
|
Another useful mechanism is the ":finally" command: >
|
||
|
|
||
|
:let tmp = tempname()
|
||
|
:try
|
||
|
: exe ".,$write " . tmp
|
||
|
: exe "!filter " . tmp
|
||
|
: .,$delete
|
||
|
: exe "$read " . tmp
|
||
|
:finally
|
||
|
: call delete(tmp)
|
||
|
:endtry
|
||
|
|
||
|
This filters the lines from the cursor until the end of the file through the
|
||
|
"filter" command, which takes a file name argument. No matter if the
|
||
|
filtering works, something goes wrong in between ":try" and ":finally" or the
|
||
|
user cancels the filtering by pressing CTRL-C, the "call delete(tmp)" is
|
||
|
always executed. This makes sure you don't leave the temporary file behind.
|
||
|
|
||
|
More information about exception handling can be found in the reference
|
||
|
manual: |exception-handling|.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.10* Various remarks
|
||
|
|
||
|
Here is a summary of items that apply to Vim scripts. They are also mentioned
|
||
|
elsewhere, but form a nice checklist.
|
||
|
|
||
|
The end-of-line character depends on the system. For Unix a single <NL>
|
||
|
character is used. For MS-DOS, Windows, OS/2 and the like, <CR><LF> is used.
|
||
|
This is important when using mappings that end in a <CR>. See |:source_crnl|.
|
||
|
|
||
|
|
||
|
WHITE SPACE
|
||
|
|
||
|
Blank lines are allowed and ignored.
|
||
|
|
||
|
Leading whitespace characters (blanks and TABs) are always ignored. The
|
||
|
whitespaces between parameters (e.g. between the 'set' and the 'cpoptions' in
|
||
|
the example below) are reduced to one blank character and plays the role of a
|
||
|
separator, the whitespaces after the last (visible) character may or may not
|
||
|
be ignored depending on the situation, see below.
|
||
|
|
||
|
For a ":set" command involving the "=" (equal) sign, such as in: >
|
||
|
|
||
|
:set cpoptions =aABceFst
|
||
|
|
||
|
the whitespace immediately before the "=" sign is ignored. But there can be
|
||
|
no whitespace after the "=" sign!
|
||
|
|
||
|
To include a whitespace character in the value of an option, it must be
|
||
|
escaped by a "\" (backslash) as in the following example: >
|
||
|
|
||
|
:set tags=my\ nice\ file
|
||
|
|
||
|
The same example written as: >
|
||
|
|
||
|
:set tags=my nice file
|
||
|
|
||
|
will issue an error, because it is interpreted as: >
|
||
|
|
||
|
:set tags=my
|
||
|
:set nice
|
||
|
:set file
|
||
|
|
||
|
|
||
|
COMMENTS
|
||
|
|
||
|
The character " (the double quote mark) starts a comment. Everything after
|
||
|
and including this character until the end-of-line is considered a comment and
|
||
|
is ignored, except for commands that don't consider comments, as shown in
|
||
|
examples below. A comment can start on any character position on the line.
|
||
|
|
||
|
There is a little "catch" with comments for some commands. Examples: >
|
||
|
|
||
|
:abbrev dev development " shorthand
|
||
|
:map <F3> o#include " insert include
|
||
|
:execute cmd " do it
|
||
|
:!ls *.c " list C files
|
||
|
|
||
|
The abbreviation 'dev' will be expanded to 'development " shorthand'. The
|
||
|
mapping of <F3> will actually be the whole line after the 'o# ....' including
|
||
|
the '" insert include'. The "execute" command will give an error. The "!"
|
||
|
command will send everything after it to the shell, causing an error for an
|
||
|
unmatched '"' character.
|
||
|
There can be no comment after ":map", ":abbreviate", ":execute" and "!"
|
||
|
commands (there are a few more commands with this restriction). For the
|
||
|
":map", ":abbreviate" and ":execute" commands there is a trick: >
|
||
|
|
||
|
:abbrev dev development|" shorthand
|
||
|
:map <F3> o#include|" insert include
|
||
|
:execute cmd |" do it
|
||
|
|
||
|
With the '|' character the command is separated from the next one. And that
|
||
|
next command is only a comment. For the last command you need to do two
|
||
|
things: |:execute| and use '|': >
|
||
|
:exe '!ls *.c' |" list C files
|
||
|
|
||
|
Notice that there is no white space before the '|' in the abbreviation and
|
||
|
mapping. For these commands, any character until the end-of-line or '|' is
|
||
|
included. As a consequence of this behavior, you don't always see that
|
||
|
trailing whitespace is included: >
|
||
|
|
||
|
:map <F4> o#include
|
||
|
|
||
|
To spot these problems, you can set the 'list' option when editing vimrc
|
||
|
files.
|
||
|
|
||
|
For Unix there is one special way to comment a line, that allows making a Vim
|
||
|
script executable: >
|
||
|
#!/usr/bin/env vim -S
|
||
|
echo "this is a Vim script"
|
||
|
quit
|
||
|
|
||
|
The "#" command by itself lists a line with the line number. Adding an
|
||
|
exclamation mark changes it into doing nothing, so that you can add the shell
|
||
|
command to execute the rest of the file. |:#!| |-S|
|
||
|
|
||
|
|
||
|
PITFALLS
|
||
|
|
||
|
Even bigger problem arises in the following example: >
|
||
|
|
||
|
:map ,ab o#include
|
||
|
:unmap ,ab
|
||
|
|
||
|
Here the unmap command will not work, because it tries to unmap ",ab ". This
|
||
|
does not exist as a mapped sequence. An error will be issued, which is very
|
||
|
hard to identify, because the ending whitespace character in ":unmap ,ab " is
|
||
|
not visible.
|
||
|
|
||
|
And this is the same as what happens when one uses a comment after an 'unmap'
|
||
|
command: >
|
||
|
|
||
|
:unmap ,ab " comment
|
||
|
|
||
|
Here the comment part will be ignored. However, Vim will try to unmap
|
||
|
',ab ', which does not exist. Rewrite it as: >
|
||
|
|
||
|
:unmap ,ab| " comment
|
||
|
|
||
|
|
||
|
RESTORING THE VIEW
|
||
|
|
||
|
Sometimes you want to make a change and go back to where the cursor was.
|
||
|
Restoring the relative position would also be nice, so that the same line
|
||
|
appears at the top of the window.
|
||
|
This example yanks the current line, puts it above the first line in the
|
||
|
file and then restores the view: >
|
||
|
|
||
|
map ,p ma"aYHmbgg"aP`bzt`a
|
||
|
|
||
|
What this does: >
|
||
|
ma"aYHmbgg"aP`bzt`a
|
||
|
< ma set mark a at cursor position
|
||
|
"aY yank current line into register a
|
||
|
Hmb go to top line in window and set mark b there
|
||
|
gg go to first line in file
|
||
|
"aP put the yanked line above it
|
||
|
`b go back to top line in display
|
||
|
zt position the text in the window as before
|
||
|
`a go back to saved cursor position
|
||
|
|
||
|
|
||
|
PACKAGING
|
||
|
|
||
|
To avoid your function names to interfere with functions that you get from
|
||
|
others, use this scheme:
|
||
|
- Prepend a unique string before each function name. I often use an
|
||
|
abbreviation. For example, "OW_" is used for the option window functions.
|
||
|
- Put the definition of your functions together in a file. Set a global
|
||
|
variable to indicate that the functions have been loaded. When sourcing the
|
||
|
file again, first unload the functions.
|
||
|
Example: >
|
||
|
|
||
|
" This is the XXX package
|
||
|
|
||
|
if exists("XXX_loaded")
|
||
|
delfun XXX_one
|
||
|
delfun XXX_two
|
||
|
endif
|
||
|
|
||
|
function XXX_one(a)
|
||
|
... body of function ...
|
||
|
endfun
|
||
|
|
||
|
function XXX_two(b)
|
||
|
... body of function ...
|
||
|
endfun
|
||
|
|
||
|
let XXX_loaded = 1
|
||
|
|
||
|
==============================================================================
|
||
|
*41.11* Writing a plugin *write-plugin*
|
||
|
|
||
|
You can write a Vim script in such a way that many people can use it. This is
|
||
|
called a plugin. Vim users can drop your script in their plugin directory and
|
||
|
use its features right away |add-plugin|.
|
||
|
|
||
|
There are actually two types of plugins:
|
||
|
|
||
|
global plugins: For all types of files.
|
||
|
filetype plugins: Only for files of a specific type.
|
||
|
|
||
|
In this section the first type is explained. Most items are also relevant for
|
||
|
writing filetype plugins. The specifics for filetype plugins are in the next
|
||
|
section |write-filetype-plugin|.
|
||
|
|
||
|
|
||
|
NAME
|
||
|
|
||
|
First of all you must choose a name for your plugin. The features provided
|
||
|
by the plugin should be clear from its name. And it should be unlikely that
|
||
|
someone else writes a plugin with the same name but which does something
|
||
|
different. And please limit the name to 8 characters, to avoid problems on
|
||
|
old Windows systems.
|
||
|
|
||
|
A script that corrects typing mistakes could be called "typecorr.vim". We
|
||
|
will use it here as an example.
|
||
|
|
||
|
For the plugin to work for everybody, it should follow a few guidelines. This
|
||
|
will be explained step-by-step. The complete example plugin is at the end.
|
||
|
|
||
|
|
||
|
BODY
|
||
|
|
||
|
Let's start with the body of the plugin, the lines that do the actual work: >
|
||
|
|
||
|
14 iabbrev teh the
|
||
|
15 iabbrev otehr other
|
||
|
16 iabbrev wnat want
|
||
|
17 iabbrev synchronisation
|
||
|
18 \ synchronization
|
||
|
19 let s:count = 4
|
||
|
|
||
|
The actual list should be much longer, of course.
|
||
|
|
||
|
The line numbers have only been added to explain a few things, don't put them
|
||
|
in your plugin file!
|
||
|
|
||
|
|
||
|
HEADER
|
||
|
|
||
|
You will probably add new corrections to the plugin and soon have several
|
||
|
versions lying around. And when distributing this file, people will want to
|
||
|
know who wrote this wonderful plugin and where they can send remarks.
|
||
|
Therefore, put a header at the top of your plugin: >
|
||
|
|
||
|
1 " Vim global plugin for correcting typing mistakes
|
||
|
2 " Last Change: 2000 Oct 15
|
||
|
3 " Maintainer: Bram Moolenaar <Bram@vim.org>
|
||
|
|
||
|
About copyright and licensing: Since plugins are very useful and it's hardly
|
||
|
worth restricting their distribution, please consider making your plugin
|
||
|
either public domain or use the Vim |license|. A short note about this near
|
||
|
the top of the plugin should be sufficient. Example: >
|
||
|
|
||
|
4 " License: This file is placed in the public domain.
|
||
|
|
||
|
|
||
|
LINE CONTINUATION, AVOIDING SIDE EFFECTS *use-cpo-save*
|
||
|
|
||
|
In line 18 above, the line-continuation mechanism is used |line-continuation|.
|
||
|
Users with 'compatible' set will run into trouble here, they will get an error
|
||
|
message. We can't just reset 'compatible', because that has a lot of side
|
||
|
effects. To avoid this, we will set the 'cpoptions' option to its Vim default
|
||
|
value and restore it later. That will allow the use of line-continuation and
|
||
|
make the script work for most people. It is done like this: >
|
||
|
|
||
|
11 let s:save_cpo = &cpo
|
||
|
12 set cpo&vim
|
||
|
..
|
||
|
42 let &cpo = s:save_cpo
|
||
|
43 unlet s:save_cpo
|
||
|
|
||
|
We first store the old value of 'cpoptions' in the s:save_cpo variable. At
|
||
|
the end of the plugin this value is restored.
|
||
|
|
||
|
Notice that a script-local variable is used |s:var|. A global variable could
|
||
|
already be in use for something else. Always use script-local variables for
|
||
|
things that are only used in the script.
|
||
|
|
||
|
|
||
|
NOT LOADING
|
||
|
|
||
|
It's possible that a user doesn't always want to load this plugin. Or the
|
||
|
system administrator has dropped it in the system-wide plugin directory, but a
|
||
|
user has his own plugin he wants to use. Then the user must have a chance to
|
||
|
disable loading this specific plugin. This will make it possible: >
|
||
|
|
||
|
6 if exists("g:loaded_typecorr")
|
||
|
7 finish
|
||
|
8 endif
|
||
|
9 let g:loaded_typecorr = 1
|
||
|
|
||
|
This also avoids that when the script is loaded twice it would cause error
|
||
|
messages for redefining functions and cause trouble for autocommands that are
|
||
|
added twice.
|
||
|
|
||
|
The name is recommended to start with "loaded_" and then the file name of the
|
||
|
plugin, literally. The "g:" is prepended just to avoid mistakes when using
|
||
|
the variable in a function (without "g:" it would be a variable local to the
|
||
|
function).
|
||
|
|
||
|
Using "finish" stops Vim from reading the rest of the file, it's much quicker
|
||
|
than using if-endif around the whole file.
|
||
|
|
||
|
|
||
|
MAPPING
|
||
|
|
||
|
Now let's make the plugin more interesting: We will add a mapping that adds a
|
||
|
correction for the word under the cursor. We could just pick a key sequence
|
||
|
for this mapping, but the user might already use it for something else. To
|
||
|
allow the user to define which keys a mapping in a plugin uses, the <Leader>
|
||
|
item can be used: >
|
||
|
|
||
|
22 map <unique> <Leader>a <Plug>TypecorrAdd
|
||
|
|
||
|
The "<Plug>TypecorrAdd" thing will do the work, more about that further on.
|
||
|
|
||
|
The user can set the "mapleader" variable to the key sequence that he wants
|
||
|
this mapping to start with. Thus if the user has done: >
|
||
|
|
||
|
let mapleader = "_"
|
||
|
|
||
|
the mapping will define "_a". If the user didn't do this, the default value
|
||
|
will be used, which is a backslash. Then a map for "\a" will be defined.
|
||
|
|
||
|
Note that <unique> is used, this will cause an error message if the mapping
|
||
|
already happened to exist. |:map-<unique>|
|
||
|
|
||
|
But what if the user wants to define his own key sequence? We can allow that
|
||
|
with this mechanism: >
|
||
|
|
||
|
21 if !hasmapto('<Plug>TypecorrAdd')
|
||
|
22 map <unique> <Leader>a <Plug>TypecorrAdd
|
||
|
23 endif
|
||
|
|
||
|
This checks if a mapping to "<Plug>TypecorrAdd" already exists, and only
|
||
|
defines the mapping from "<Leader>a" if it doesn't. The user then has a
|
||
|
chance of putting this in his vimrc file: >
|
||
|
|
||
|
map ,c <Plug>TypecorrAdd
|
||
|
|
||
|
Then the mapped key sequence will be ",c" instead of "_a" or "\a".
|
||
|
|
||
|
|
||
|
PIECES
|
||
|
|
||
|
If a script gets longer, you often want to break up the work in pieces. You
|
||
|
can use functions or mappings for this. But you don't want these functions
|
||
|
and mappings to interfere with the ones from other scripts. For example, you
|
||
|
could define a function Add(), but another script could try to define the same
|
||
|
function. To avoid this, we define the function local to the script by
|
||
|
prepending it with "s:".
|
||
|
|
||
|
We will define a function that adds a new typing correction: >
|
||
|
|
||
|
30 function s:Add(from, correct)
|
||
|
31 let to = input("type the correction for " . a:from . ": ")
|
||
|
32 exe ":iabbrev " . a:from . " " . to
|
||
|
..
|
||
|
36 endfunction
|
||
|
|
||
|
Now we can call the function s:Add() from within this script. If another
|
||
|
script also defines s:Add(), it will be local to that script and can only
|
||
|
be called from the script it was defined in. There can also be a global Add()
|
||
|
function (without the "s:"), which is again another function.
|
||
|
|
||
|
<SID> can be used with mappings. It generates a script ID, which identifies
|
||
|
the current script. In our typing correction plugin we use it like this: >
|
||
|
|
||
|
24 noremap <unique> <script> <Plug>TypecorrAdd <SID>Add
|
||
|
..
|
||
|
28 noremap <SID>Add :call <SID>Add(expand("<cword>"), 1)<CR>
|
||
|
|
||
|
Thus when a user types "\a", this sequence is invoked: >
|
||
|
|
||
|
\a -> <Plug>TypecorrAdd -> <SID>Add -> :call <SID>Add()
|
||
|
|
||
|
If another script would also map <SID>Add, it would get another script ID and
|
||
|
thus define another mapping.
|
||
|
|
||
|
Note that instead of s:Add() we use <SID>Add() here. That is because the
|
||
|
mapping is typed by the user, thus outside of the script. The <SID> is
|
||
|
translated to the script ID, so that Vim knows in which script to look for
|
||
|
the Add() function.
|
||
|
|
||
|
This is a bit complicated, but it's required for the plugin to work together
|
||
|
with other plugins. The basic rule is that you use <SID>Add() in mappings and
|
||
|
s:Add() in other places (the script itself, autocommands, user commands).
|
||
|
|
||
|
We can also add a menu entry to do the same as the mapping: >
|
||
|
|
||
|
26 noremenu <script> Plugin.Add\ Correction <SID>Add
|
||
|
|
||
|
The "Plugin" menu is recommended for adding menu items for plugins. In this
|
||
|
case only one item is used. When adding more items, creating a submenu is
|
||
|
recommended. For example, "Plugin.CVS" could be used for a plugin that offers
|
||
|
CVS operations "Plugin.CVS.checkin", "Plugin.CVS.checkout", etc.
|
||
|
|
||
|
Note that in line 28 ":noremap" is used to avoid that any other mappings cause
|
||
|
trouble. Someone may have remapped ":call", for example. In line 24 we also
|
||
|
use ":noremap", but we do want "<SID>Add" to be remapped. This is why
|
||
|
"<script>" is used here. This only allows mappings which are local to the
|
||
|
script. |:map-<script>| The same is done in line 26 for ":noremenu".
|
||
|
|:menu-<script>|
|
||
|
|
||
|
|
||
|
<SID> AND <Plug> *using-<Plug>*
|
||
|
|
||
|
Both <SID> and <Plug> are used to avoid that mappings of typed keys interfere
|
||
|
with mappings that are only to be used from other mappings. Note the
|
||
|
difference between using <SID> and <Plug>:
|
||
|
|
||
|
<Plug> is visible outside of the script. It is used for mappings which the
|
||
|
user might want to map a key sequence to. <Plug> is a special code
|
||
|
that a typed key will never produce.
|
||
|
To make it very unlikely that other plugins use the same sequence of
|
||
|
characters, use this structure: <Plug> scriptname mapname
|
||
|
In our example the scriptname is "Typecorr" and the mapname is "Add".
|
||
|
This results in "<Plug>TypecorrAdd". Only the first character of
|
||
|
scriptname and mapname is uppercase, so that we can see where mapname
|
||
|
starts.
|
||
|
|
||
|
<SID> is the script ID, a unique identifier for a script.
|
||
|
Internally Vim translates <SID> to "<SNR>123_", where "123" can be any
|
||
|
number. Thus a function "<SID>Add()" will have a name "<SNR>11_Add()"
|
||
|
in one script, and "<SNR>22_Add()" in another. You can see this if
|
||
|
you use the ":function" command to get a list of functions. The
|
||
|
translation of <SID> in mappings is exactly the same, that's how you
|
||
|
can call a script-local function from a mapping.
|
||
|
|
||
|
|
||
|
USER COMMAND
|
||
|
|
||
|
Now let's add a user command to add a correction: >
|
||
|
|
||
|
38 if !exists(":Correct")
|
||
|
39 command -nargs=1 Correct :call s:Add(<q-args>, 0)
|
||
|
40 endif
|
||
|
|
||
|
The user command is defined only if no command with the same name already
|
||
|
exists. Otherwise we would get an error here. Overriding the existing user
|
||
|
command with ":command!" is not a good idea, this would probably make the user
|
||
|
wonder why the command he defined himself doesn't work. |:command|
|
||
|
|
||
|
|
||
|
SCRIPT VARIABLES
|
||
|
|
||
|
When a variable starts with "s:" it is a script variable. It can only be used
|
||
|
inside a script. Outside the script it's not visible. This avoids trouble
|
||
|
with using the same variable name in different scripts. The variables will be
|
||
|
kept as long as Vim is running. And the same variables are used when sourcing
|
||
|
the same script again. |s:var|
|
||
|
|
||
|
The fun is that these variables can also be used in functions, autocommands
|
||
|
and user commands that are defined in the script. In our example we can add
|
||
|
a few lines to count the number of corrections: >
|
||
|
|
||
|
19 let s:count = 4
|
||
|
..
|
||
|
30 function s:Add(from, correct)
|
||
|
..
|
||
|
34 let s:count = s:count + 1
|
||
|
35 echo s:count . " corrections now"
|
||
|
36 endfunction
|
||
|
|
||
|
First s:count is initialized to 4 in the script itself. When later the
|
||
|
s:Add() function is called, it increments s:count. It doesn't matter from
|
||
|
where the function was called, since it has been defined in the script, it
|
||
|
will use the local variables from this script.
|
||
|
|
||
|
|
||
|
THE RESULT
|
||
|
|
||
|
Here is the resulting complete example: >
|
||
|
|
||
|
1 " Vim global plugin for correcting typing mistakes
|
||
|
2 " Last Change: 2000 Oct 15
|
||
|
3 " Maintainer: Bram Moolenaar <Bram@vim.org>
|
||
|
4 " License: This file is placed in the public domain.
|
||
|
5
|
||
|
6 if exists("g:loaded_typecorr")
|
||
|
7 finish
|
||
|
8 endif
|
||
|
9 let g:loaded_typecorr = 1
|
||
|
10
|
||
|
11 let s:save_cpo = &cpo
|
||
|
12 set cpo&vim
|
||
|
13
|
||
|
14 iabbrev teh the
|
||
|
15 iabbrev otehr other
|
||
|
16 iabbrev wnat want
|
||
|
17 iabbrev synchronisation
|
||
|
18 \ synchronization
|
||
|
19 let s:count = 4
|
||
|
20
|
||
|
21 if !hasmapto('<Plug>TypecorrAdd')
|
||
|
22 map <unique> <Leader>a <Plug>TypecorrAdd
|
||
|
23 endif
|
||
|
24 noremap <unique> <script> <Plug>TypecorrAdd <SID>Add
|
||
|
25
|
||
|
26 noremenu <script> Plugin.Add\ Correction <SID>Add
|
||
|
27
|
||
|
28 noremap <SID>Add :call <SID>Add(expand("<cword>"), 1)<CR>
|
||
|
29
|
||
|
30 function s:Add(from, correct)
|
||
|
31 let to = input("type the correction for " . a:from . ": ")
|
||
|
32 exe ":iabbrev " . a:from . " " . to
|
||
|
33 if a:correct | exe "normal viws\<C-R>\" \b\e" | endif
|
||
|
34 let s:count = s:count + 1
|
||
|
35 echo s:count . " corrections now"
|
||
|
36 endfunction
|
||
|
37
|
||
|
38 if !exists(":Correct")
|
||
|
39 command -nargs=1 Correct :call s:Add(<q-args>, 0)
|
||
|
40 endif
|
||
|
41
|
||
|
42 let &cpo = s:save_cpo
|
||
|
43 unlet s:save_cpo
|
||
|
|
||
|
Line 33 wasn't explained yet. It applies the new correction to the word under
|
||
|
the cursor. The |:normal| command is used to use the new abbreviation. Note
|
||
|
that mappings and abbreviations are expanded here, even though the function
|
||
|
was called from a mapping defined with ":noremap".
|
||
|
|
||
|
Using "unix" for the 'fileformat' option is recommended. The Vim scripts will
|
||
|
then work everywhere. Scripts with 'fileformat' set to "dos" do not work on
|
||
|
Unix. Also see |:source_crnl|. To be sure it is set right, do this before
|
||
|
writing the file: >
|
||
|
|
||
|
:set fileformat=unix
|
||
|
|
||
|
|
||
|
DOCUMENTATION *write-local-help*
|
||
|
|
||
|
It's a good idea to also write some documentation for your plugin. Especially
|
||
|
when its behavior can be changed by the user. See |add-local-help| for how
|
||
|
they are installed.
|
||
|
|
||
|
Here is a simple example for a plugin help file, called "typecorr.txt": >
|
||
|
|
||
|
1 *typecorr.txt* Plugin for correcting typing mistakes
|
||
|
2
|
||
|
3 If you make typing mistakes, this plugin will have them corrected
|
||
|
4 automatically.
|
||
|
5
|
||
|
6 There are currently only a few corrections. Add your own if you like.
|
||
|
7
|
||
|
8 Mappings:
|
||
|
9 <Leader>a or <Plug>TypecorrAdd
|
||
|
10 Add a correction for the word under the cursor.
|
||
|
11
|
||
|
12 Commands:
|
||
|
13 :Correct {word}
|
||
|
14 Add a correction for {word}.
|
||
|
15
|
||
|
16 *typecorr-settings*
|
||
|
17 This plugin doesn't have any settings.
|
||
|
|
||
|
The first line is actually the only one for which the format matters. It will
|
||
|
be extracted from the help file to be put in the "LOCAL ADDITIONS:" section of
|
||
|
help.txt |local-additions|. The first "*" must be in the first column of the
|
||
|
first line. After adding your help file do ":help" and check that the entries
|
||
|
line up nicely.
|
||
|
|
||
|
You can add more tags inside ** in your help file. But be careful not to use
|
||
|
existing help tags. You would probably use the name of your plugin in most of
|
||
|
them, like "typecorr-settings" in the example.
|
||
|
|
||
|
Using references to other parts of the help in || is recommended. This makes
|
||
|
it easy for the user to find associated help.
|
||
|
|
||
|
|
||
|
FILETYPE DETECTION *plugin-filetype*
|
||
|
|
||
|
If your filetype is not already detected by Vim, you should create a filetype
|
||
|
detection snippet in a separate file. It is usually in the form of an
|
||
|
autocommand that sets the filetype when the file name matches a pattern.
|
||
|
Example: >
|
||
|
|
||
|
au BufNewFile,BufRead *.foo set filetype=foofoo
|
||
|
|
||
|
Write this single-line file as "ftdetect/foofoo.vim" in the first directory
|
||
|
that appears in 'runtimepath'. For Unix that would be
|
||
|
"~/.vim/ftdetect/foofoo.vim". The convention is to use the name of the
|
||
|
filetype for the script name.
|
||
|
|
||
|
You can make more complicated checks if you like, for example to inspect the
|
||
|
contents of the file to recognize the language. Also see |new-filetype|.
|
||
|
|
||
|
|
||
|
SUMMARY *plugin-special*
|
||
|
|
||
|
Summary of special things to use in a plugin:
|
||
|
|
||
|
s:name Variables local to the script.
|
||
|
|
||
|
<SID> Script-ID, used for mappings and functions local to
|
||
|
the script.
|
||
|
|
||
|
hasmapto() Function to test if the user already defined a mapping
|
||
|
for functionality the script offers.
|
||
|
|
||
|
<Leader> Value of "mapleader", which the user defines as the
|
||
|
keys that plugin mappings start with.
|
||
|
|
||
|
:map <unique> Give a warning if a mapping already exists.
|
||
|
|
||
|
:noremap <script> Use only mappings local to the script, not global
|
||
|
mappings.
|
||
|
|
||
|
exists(":Cmd") Check if a user command already exists.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.12* Writing a filetype plugin *write-filetype-plugin* *ftplugin*
|
||
|
|
||
|
A filetype plugin is like a global plugin, except that it sets options and
|
||
|
defines mappings for the current buffer only. See |add-filetype-plugin| for
|
||
|
how this type of plugin is used.
|
||
|
|
||
|
First read the section on global plugins above |41.11|. All that is said there
|
||
|
also applies to filetype plugins. There are a few extras, which are explained
|
||
|
here. The essential thing is that a filetype plugin should only have an
|
||
|
effect on the current buffer.
|
||
|
|
||
|
|
||
|
DISABLING
|
||
|
|
||
|
If you are writing a filetype plugin to be used by many people, they need a
|
||
|
chance to disable loading it. Put this at the top of the plugin: >
|
||
|
|
||
|
" Only do this when not done yet for this buffer
|
||
|
if exists("b:did_ftplugin")
|
||
|
finish
|
||
|
endif
|
||
|
let b:did_ftplugin = 1
|
||
|
|
||
|
This also needs to be used to avoid that the same plugin is executed twice for
|
||
|
the same buffer (happens when using an ":edit" command without arguments).
|
||
|
|
||
|
Now users can disable loading the default plugin completely by making a
|
||
|
filetype plugin with only this line: >
|
||
|
|
||
|
let b:did_ftplugin = 1
|
||
|
|
||
|
This does require that the filetype plugin directory comes before $VIMRUNTIME
|
||
|
in 'runtimepath'!
|
||
|
|
||
|
If you do want to use the default plugin, but overrule one of the settings,
|
||
|
you can write the different setting in a script: >
|
||
|
|
||
|
setlocal textwidth=70
|
||
|
|
||
|
Now write this in the "after" directory, so that it gets sourced after the
|
||
|
distributed "vim.vim" ftplugin |after-directory|. For Unix this would be
|
||
|
"~/.vim/after/ftplugin/vim.vim". Note that the default plugin will have set
|
||
|
"b:did_ftplugin", but it is ignored here.
|
||
|
|
||
|
|
||
|
OPTIONS
|
||
|
|
||
|
To make sure the filetype plugin only affects the current buffer use the >
|
||
|
|
||
|
:setlocal
|
||
|
|
||
|
command to set options. And only set options which are local to a buffer (see
|
||
|
the help for the option to check that). When using |:setlocal| for global
|
||
|
options or options local to a window, the value will change for many buffers,
|
||
|
and that is not what a filetype plugin should do.
|
||
|
|
||
|
When an option has a value that is a list of flags or items, consider using
|
||
|
"+=" and "-=" to keep the existing value. Be aware that the user may have
|
||
|
changed an option value already. First resetting to the default value and
|
||
|
then changing it is often a good idea. Example: >
|
||
|
|
||
|
:setlocal formatoptions& formatoptions+=ro
|
||
|
|
||
|
|
||
|
MAPPINGS
|
||
|
|
||
|
To make sure mappings will only work in the current buffer use the >
|
||
|
|
||
|
:map <buffer>
|
||
|
|
||
|
command. This needs to be combined with the two-step mapping explained above.
|
||
|
An example of how to define functionality in a filetype plugin: >
|
||
|
|
||
|
if !hasmapto('<Plug>JavaImport')
|
||
|
map <buffer> <unique> <LocalLeader>i <Plug>JavaImport
|
||
|
endif
|
||
|
noremap <buffer> <unique> <Plug>JavaImport oimport ""<Left><Esc>
|
||
|
|
||
|
|hasmapto()| is used to check if the user has already defined a map to
|
||
|
<Plug>JavaImport. If not, then the filetype plugin defines the default
|
||
|
mapping. This starts with |<LocalLeader>|, which allows the user to select
|
||
|
the key(s) he wants filetype plugin mappings to start with. The default is a
|
||
|
backslash.
|
||
|
"<unique>" is used to give an error message if the mapping already exists or
|
||
|
overlaps with an existing mapping.
|
||
|
|:noremap| is used to avoid that any other mappings that the user has defined
|
||
|
interferes. You might want to use ":noremap <script>" to allow remapping
|
||
|
mappings defined in this script that start with <SID>.
|
||
|
|
||
|
The user must have a chance to disable the mappings in a filetype plugin,
|
||
|
without disabling everything. Here is an example of how this is done for a
|
||
|
plugin for the mail filetype: >
|
||
|
|
||
|
" Add mappings, unless the user didn't want this.
|
||
|
if !exists("no_plugin_maps") && !exists("no_mail_maps")
|
||
|
" Quote text by inserting "> "
|
||
|
if !hasmapto('<Plug>MailQuote')
|
||
|
vmap <buffer> <LocalLeader>q <Plug>MailQuote
|
||
|
nmap <buffer> <LocalLeader>q <Plug>MailQuote
|
||
|
endif
|
||
|
vnoremap <buffer> <Plug>MailQuote :s/^/> /<CR>
|
||
|
nnoremap <buffer> <Plug>MailQuote :.,$s/^/> /<CR>
|
||
|
endif
|
||
|
|
||
|
Two global variables are used:
|
||
|
no_plugin_maps disables mappings for all filetype plugins
|
||
|
no_mail_maps disables mappings for a specific filetype
|
||
|
|
||
|
|
||
|
USER COMMANDS
|
||
|
|
||
|
To add a user command for a specific file type, so that it can only be used in
|
||
|
one buffer, use the "-buffer" argument to |:command|. Example: >
|
||
|
|
||
|
:command -buffer Make make %:r.s
|
||
|
|
||
|
|
||
|
VARIABLES
|
||
|
|
||
|
A filetype plugin will be sourced for each buffer of the type it's for. Local
|
||
|
script variables |s:var| will be shared between all invocations. Use local
|
||
|
buffer variables |b:var| if you want a variable specifically for one buffer.
|
||
|
|
||
|
|
||
|
FUNCTIONS
|
||
|
|
||
|
When defining a function, this only needs to be done once. But the filetype
|
||
|
plugin will be sourced every time a file with this filetype will be opened.
|
||
|
This construct makes sure the function is only defined once: >
|
||
|
|
||
|
:if !exists("*s:Func")
|
||
|
: function s:Func(arg)
|
||
|
: ...
|
||
|
: endfunction
|
||
|
:endif
|
||
|
<
|
||
|
|
||
|
UNDO *undo_ftplugin*
|
||
|
|
||
|
When the user does ":setfiletype xyz" the effect of the previous filetype
|
||
|
should be undone. Set the b:undo_ftplugin variable to the commands that will
|
||
|
undo the settings in your filetype plugin. Example: >
|
||
|
|
||
|
let b:undo_ftplugin = "setlocal fo< com< tw< commentstring<"
|
||
|
\ . "| unlet b:match_ignorecase b:match_words b:match_skip"
|
||
|
|
||
|
Using ":setlocal" with "<" after the option name resets the option to its
|
||
|
global value. That is mostly the best way to reset the option value.
|
||
|
|
||
|
This does require removing the "C" flag from 'cpoptions' to allow line
|
||
|
continuation, as mentioned above |use-cpo-save|.
|
||
|
|
||
|
|
||
|
FILE NAME
|
||
|
|
||
|
The filetype must be included in the file name |ftplugin-name|. Use one of
|
||
|
these three forms:
|
||
|
|
||
|
.../ftplugin/stuff.vim
|
||
|
.../ftplugin/stuff_foo.vim
|
||
|
.../ftplugin/stuff/bar.vim
|
||
|
|
||
|
"stuff" is the filetype, "foo" and "bar" are arbitrary names.
|
||
|
|
||
|
|
||
|
SUMMARY *ftplugin-special*
|
||
|
|
||
|
Summary of special things to use in a filetype plugin:
|
||
|
|
||
|
<LocalLeader> Value of "maplocalleader", which the user defines as
|
||
|
the keys that filetype plugin mappings start with.
|
||
|
|
||
|
:map <buffer> Define a mapping local to the buffer.
|
||
|
|
||
|
:noremap <script> Only remap mappings defined in this script that start
|
||
|
with <SID>.
|
||
|
|
||
|
:setlocal Set an option for the current buffer only.
|
||
|
|
||
|
:command -buffer Define a user command local to the buffer.
|
||
|
|
||
|
exists("*s:Func") Check if a function was already defined.
|
||
|
|
||
|
Also see |plugin-special|, the special things used for all plugins.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.13* Writing a compiler plugin *write-compiler-plugin*
|
||
|
|
||
|
A compiler plugin sets options for use with a specific compiler. The user can
|
||
|
load it with the |:compiler| command. The main use is to set the
|
||
|
'errorformat' and 'makeprg' options.
|
||
|
|
||
|
Easiest is to have a look at examples. This command will edit all the default
|
||
|
compiler plugins: >
|
||
|
|
||
|
:next $VIMRUNTIME/compiler/*.vim
|
||
|
|
||
|
Use |:next| to go to the next plugin file.
|
||
|
|
||
|
There are two special items about these files. First is a mechanism to allow
|
||
|
a user to overrule or add to the default file. The default files start with: >
|
||
|
|
||
|
:if exists("current_compiler")
|
||
|
: finish
|
||
|
:endif
|
||
|
:let current_compiler = "mine"
|
||
|
|
||
|
When you write a compiler file and put it in your personal runtime directory
|
||
|
(e.g., ~/.vim/compiler for Unix), you set the "current_compiler" variable to
|
||
|
make the default file skip the settings.
|
||
|
*:CompilerSet*
|
||
|
The second mechanism is to use ":set" for ":compiler!" and ":setlocal" for
|
||
|
":compiler". Vim defines the ":CompilerSet" user command for this. However,
|
||
|
older Vim versions don't, thus your plugin should define it then. This is an
|
||
|
example: >
|
||
|
|
||
|
if exists(":CompilerSet") != 2
|
||
|
command -nargs=* CompilerSet setlocal <args>
|
||
|
endif
|
||
|
CompilerSet errorformat& " use the default 'errorformat'
|
||
|
CompilerSet makeprg=nmake
|
||
|
|
||
|
When you write a compiler plugin for the Vim distribution or for a system-wide
|
||
|
runtime directory, use the mechanism mentioned above. When
|
||
|
"current_compiler" was already set by a user plugin nothing will be done.
|
||
|
|
||
|
When you write a compiler plugin to overrule settings from a default plugin,
|
||
|
don't check "current_compiler". This plugin is supposed to be loaded
|
||
|
last, thus it should be in a directory at the end of 'runtimepath'. For Unix
|
||
|
that could be ~/.vim/after/compiler.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.14* Writing a plugin that loads quickly *write-plugin-quickload*
|
||
|
|
||
|
A plugin may grow and become quite long. The startup delay may become
|
||
|
noticeable, while you hardly ever use the plugin. Then it's time for a
|
||
|
quickload plugin.
|
||
|
|
||
|
The basic idea is that the plugin is loaded twice. The first time user
|
||
|
commands and mappings are defined that offer the functionality. The second
|
||
|
time the functions that implement the functionality are defined.
|
||
|
|
||
|
It may sound surprising that quickload means loading a script twice. What we
|
||
|
mean is that it loads quickly the first time, postponing the bulk of the
|
||
|
script to the second time, which only happens when you actually use it. When
|
||
|
you always use the functionality it actually gets slower!
|
||
|
|
||
|
Note that since Vim 7 there is an alternative: use the |autoload|
|
||
|
functionality |41.15|.
|
||
|
|
||
|
The following example shows how it's done: >
|
||
|
|
||
|
" Vim global plugin for demonstrating quick loading
|
||
|
" Last Change: 2005 Feb 25
|
||
|
" Maintainer: Bram Moolenaar <Bram@vim.org>
|
||
|
" License: This file is placed in the public domain.
|
||
|
|
||
|
if !exists("s:did_load")
|
||
|
command -nargs=* BNRead call BufNetRead(<f-args>)
|
||
|
map <F19> :call BufNetWrite('something')<CR>
|
||
|
|
||
|
let s:did_load = 1
|
||
|
exe 'au FuncUndefined BufNet* source ' . expand('<sfile>')
|
||
|
finish
|
||
|
endif
|
||
|
|
||
|
function BufNetRead(...)
|
||
|
echo 'BufNetRead(' . string(a:000) . ')'
|
||
|
" read functionality here
|
||
|
endfunction
|
||
|
|
||
|
function BufNetWrite(...)
|
||
|
echo 'BufNetWrite(' . string(a:000) . ')'
|
||
|
" write functionality here
|
||
|
endfunction
|
||
|
|
||
|
When the script is first loaded "s:did_load" is not set. The commands between
|
||
|
the "if" and "endif" will be executed. This ends in a |:finish| command, thus
|
||
|
the rest of the script is not executed.
|
||
|
|
||
|
The second time the script is loaded "s:did_load" exists and the commands
|
||
|
after the "endif" are executed. This defines the (possible long)
|
||
|
BufNetRead() and BufNetWrite() functions.
|
||
|
|
||
|
If you drop this script in your plugin directory Vim will execute it on
|
||
|
startup. This is the sequence of events that happens:
|
||
|
|
||
|
1. The "BNRead" command is defined and the <F19> key is mapped when the script
|
||
|
is sourced at startup. A |FuncUndefined| autocommand is defined. The
|
||
|
":finish" command causes the script to terminate early.
|
||
|
|
||
|
2. The user types the BNRead command or presses the <F19> key. The
|
||
|
BufNetRead() or BufNetWrite() function will be called.
|
||
|
|
||
|
3. Vim can't find the function and triggers the |FuncUndefined| autocommand
|
||
|
event. Since the pattern "BufNet*" matches the invoked function, the
|
||
|
command "source fname" will be executed. "fname" will be equal to the name
|
||
|
of the script, no matter where it is located, because it comes from
|
||
|
expanding "<sfile>" (see |expand()|).
|
||
|
|
||
|
4. The script is sourced again, the "s:did_load" variable exists and the
|
||
|
functions are defined.
|
||
|
|
||
|
Notice that the functions that are loaded afterwards match the pattern in the
|
||
|
|FuncUndefined| autocommand. You must make sure that no other plugin defines
|
||
|
functions that match this pattern.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.15* Writing library scripts *write-library-script*
|
||
|
|
||
|
Some functionality will be required in several places. When this becomes more
|
||
|
than a few lines you will want to put it in one script and use it from many
|
||
|
scripts. We will call that one script a library script.
|
||
|
|
||
|
Manually loading a library script is possible, so long as you avoid loading it
|
||
|
when it's already done. You can do this with the |exists()| function.
|
||
|
Example: >
|
||
|
|
||
|
if !exists('*MyLibFunction')
|
||
|
runtime library/mylibscript.vim
|
||
|
endif
|
||
|
call MyLibFunction(arg)
|
||
|
|
||
|
Here you need to know that MyLibFunction() is defined in a script
|
||
|
"library/mylibscript.vim" in one of the directories in 'runtimepath'.
|
||
|
|
||
|
To make this a bit simpler Vim offers the autoload mechanism. Then the
|
||
|
example looks like this: >
|
||
|
|
||
|
call mylib#myfunction(arg)
|
||
|
|
||
|
That's a lot simpler, isn't it? Vim will recognize the function name and when
|
||
|
it's not defined search for the script "autoload/mylib.vim" in 'runtimepath'.
|
||
|
That script must define the "mylib#myfunction()" function.
|
||
|
|
||
|
You can put many other functions in the mylib.vim script, you are free to
|
||
|
organize your functions in library scripts. But you must use function names
|
||
|
where the part before the '#' matches the script name. Otherwise Vim would
|
||
|
not know what script to load.
|
||
|
|
||
|
If you get really enthusiastic and write lots of library scripts, you may
|
||
|
want to use subdirectories. Example: >
|
||
|
|
||
|
call netlib#ftp#read('somefile')
|
||
|
|
||
|
For Unix the library script used for this could be:
|
||
|
|
||
|
~/.vim/autoload/netlib/ftp.vim
|
||
|
|
||
|
Where the function is defined like this: >
|
||
|
|
||
|
function netlib#ftp#read(fname)
|
||
|
" Read the file fname through ftp
|
||
|
endfunction
|
||
|
|
||
|
Notice that the name the function is defined with is exactly the same as the
|
||
|
name used for calling the function. And the part before the last '#'
|
||
|
exactly matches the subdirectory and script name.
|
||
|
|
||
|
You can use the same mechanism for variables: >
|
||
|
|
||
|
let weekdays = dutch#weekdays
|
||
|
|
||
|
This will load the script "autoload/dutch.vim", which should contain something
|
||
|
like: >
|
||
|
|
||
|
let dutch#weekdays = ['zondag', 'maandag', 'dinsdag', 'woensdag',
|
||
|
\ 'donderdag', 'vrijdag', 'zaterdag']
|
||
|
|
||
|
Further reading: |autoload|.
|
||
|
|
||
|
==============================================================================
|
||
|
*41.16* Distributing Vim scripts *distribute-script*
|
||
|
|
||
|
Vim users will look for scripts on the Vim website: http://www.vim.org.
|
||
|
If you made something that is useful for others, share it!
|
||
|
|
||
|
Vim scripts can be used on any system. There might not be a tar or gzip
|
||
|
command. If you want to pack files together and/or compress them the "zip"
|
||
|
utility is recommended.
|
||
|
|
||
|
For utmost portability use Vim itself to pack scripts together. This can be
|
||
|
done with the Vimball utility. See |vimball|.
|
||
|
|
||
|
It's good if you add a line to allow automatic updating. See |glvs-plugins|.
|
||
|
|
||
|
==============================================================================
|
||
|
|
||
|
Next chapter: |usr_42.txt| Add new menus
|
||
|
|
||
|
Copyright: see |manual-copyright| vim:tw=78:ts=8:ft=help:norl:
|