2020-05-01 16:07:38 +02:00
|
|
|
*vim9.txt* For Vim version 8.2. Last change: 2020 Apr 30
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
VIM REFERENCE MANUAL by Bram Moolenaar
|
|
|
|
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
Vim9 script commands and expressions. *vim9*
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Most expression help is in |eval.txt|. This file is about the new syntax and
|
|
|
|
features in Vim9 script.
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
|
|
|
|
|
|
|
1 What is Vim9 script? |vim9-script|
|
|
|
|
2. Differences |vim9-differences|
|
|
|
|
3. New style functions |fast-functions|
|
|
|
|
4. Types |vim9-types|
|
|
|
|
5. Namespace, Import and Export |vim9script|
|
|
|
|
|
|
|
|
9. Rationale |vim9-rationale|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
1. What is Vim9 script? *vim9-script*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
Vim script has been growing over time, while preserving backwards
|
|
|
|
compatibility. That means bad choices from the past often can't be changed
|
|
|
|
and compability with Vi restricts possible solutions. Execution is quite
|
|
|
|
slow, each line is parsed every time it is executed.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
The main goal of Vim9 script is to drastically improve performance. This is
|
|
|
|
accomplished by compiling commands into instructions that can be efficiently
|
|
|
|
executed. An increase in execution speed of 10 to 100 times can be expected.
|
|
|
|
|
|
|
|
A secondary goal is to avoid Vim-specific constructs and get closer to
|
|
|
|
commonly used programming languages, such as JavaScript, TypeScript and Java.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
The performance improvements can only be achieved by not being 100% backwards
|
|
|
|
compatible. For example, in a function the arguments are not available in the
|
2020-05-01 16:07:38 +02:00
|
|
|
"a:" dictionary, because creating that dictionary adds quite a lot of
|
|
|
|
overhead. Other differences are more subtle, such as how errors are handled.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
The Vim9 script syntax and semantics are used in:
|
|
|
|
- a function defined with the `:def` command
|
|
|
|
- a script file where the first command is `vim9script`
|
|
|
|
|
|
|
|
When using `:function` in a Vim9 script file the legacy syntax is used.
|
2020-05-01 16:07:38 +02:00
|
|
|
However, this can be confusing and is therefore discouraged.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
Vim9 script and legacy Vim script can be mixed. There is no requirement to
|
|
|
|
rewrite old scripts, they keep working as before.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
2. Differences from legacy Vim script *vim9-differences*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
2020-04-13 14:41:35 +02:00
|
|
|
Comments starting with # ~
|
|
|
|
|
|
|
|
In Vim script comments normally start with double quote. That can also be the
|
|
|
|
start of a string, thus in many places it cannot be used. In Vim9 script a
|
2020-05-01 16:07:38 +02:00
|
|
|
comment can also start with #. In Vi this is a command to list text with
|
2020-04-13 14:41:35 +02:00
|
|
|
numbers, but you can also use `:number` for that. >
|
2020-05-01 16:07:38 +02:00
|
|
|
let count = 0 # number of occurences
|
2020-04-13 14:41:35 +02:00
|
|
|
|
2020-04-20 19:52:53 +02:00
|
|
|
To improve readability there must be a space between the command and the #
|
|
|
|
that starts a comment. Note that #{ is the start of a dictionary, therefore
|
|
|
|
it cannot start a comment.
|
|
|
|
|
2020-04-13 14:41:35 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
Vim9 functions ~
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
A function defined with `:def` is compiled. Execution is many times faster,
|
|
|
|
often 10x to 100x times.
|
|
|
|
|
|
|
|
Many errors are already found when compiling, before the function is called.
|
|
|
|
The syntax is strict, to enforce code that is easy to read and understand.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
`:def` has no extra arguments like `:function` does: "range", "abort", "dict"
|
|
|
|
or "closure". A `:def` function always aborts on an error, does not get a
|
|
|
|
range passed and cannot be a "dict" function.
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
The argument types and return type need to be specified. The "any" type can
|
|
|
|
be used, type checking will then be done at runtime, like with legacy
|
|
|
|
functions.
|
|
|
|
|
|
|
|
Arguments are accessed by name, without "a:". There is no "a:" dictionary or
|
|
|
|
"a:000" list.
|
|
|
|
|
|
|
|
Variable arguments are defined as the last argument, with a name and have a
|
|
|
|
list type, similar to Typescript. For example, a list of numbers: >
|
|
|
|
def MyFunc(...itemlist: list<number>)
|
2020-01-26 15:56:19 +01:00
|
|
|
for item in itemlist
|
|
|
|
...
|
|
|
|
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
Functions and variables are script-local by default ~
|
2020-04-20 19:52:53 +02:00
|
|
|
|
|
|
|
When using `:function` or `:def` to specify a new function at the script level
|
|
|
|
in a Vim9 script, the function is local to the script, as if "s:" was
|
2020-05-01 16:07:38 +02:00
|
|
|
prefixed. Using the "s:" prefix is optional.
|
|
|
|
|
|
|
|
To define or use a global function or variable the "g:" prefix must be used.
|
2020-04-20 19:52:53 +02:00
|
|
|
|
|
|
|
When using `:function` or `:def` to specify a new function inside a function,
|
|
|
|
the function is local to the function. It is not possible to define a
|
2020-05-01 16:07:38 +02:00
|
|
|
script-local function inside a function. It is possible to define a global
|
|
|
|
function, using the "g:" prefix.
|
2020-04-20 19:52:53 +02:00
|
|
|
|
|
|
|
When referring to a function and no "s:" or "g:" prefix is used, Vim will
|
|
|
|
search for the function in this order:
|
2020-05-01 16:07:38 +02:00
|
|
|
- Local to the current scope and outer scopes up to the function scope.
|
2020-04-20 19:52:53 +02:00
|
|
|
- Local to the current script file.
|
|
|
|
- Imported functions, see `:import`.
|
2020-05-01 16:07:38 +02:00
|
|
|
In all cases the function must be defined before used. To make a call cycle a
|
|
|
|
global function needs to be used. (TODO: can we fix this?)
|
|
|
|
|
|
|
|
The result is that functions and variables without a namespace can always be
|
|
|
|
found in the script, either defined there or imported. Global functions and
|
|
|
|
variables could be defined anywhere (good luck finding where!).
|
2020-04-20 19:52:53 +02:00
|
|
|
|
|
|
|
Global functions can be defined and deleted at nearly any time. In Vim9
|
|
|
|
script script-local functions are defined once when the script is sourced and
|
2020-05-01 16:07:38 +02:00
|
|
|
cannot be deleted. Except that when the same script is sourced again all
|
|
|
|
existing script-local functions and variables are deleted.
|
2020-04-20 19:52:53 +02:00
|
|
|
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
Variable declarations with :let and :const ~
|
|
|
|
|
|
|
|
Local variables need to be declared with `:let`. Local constants need to be
|
|
|
|
declared with `:const`. We refer to both as "variables".
|
|
|
|
|
|
|
|
Variables can be local to a script, function or code block: >
|
|
|
|
vim9script
|
|
|
|
let script_var = 123
|
|
|
|
def SomeFunc()
|
|
|
|
let func_var = script_var
|
|
|
|
if cond
|
|
|
|
let block_var = func_var
|
|
|
|
...
|
|
|
|
|
|
|
|
The variables are only visible in the block where they are defined and nested
|
|
|
|
blocks. Once the block ends the variable is no longer accessible: >
|
|
|
|
if cond
|
|
|
|
let inner = 5
|
|
|
|
else
|
|
|
|
let inner = 0
|
|
|
|
endif
|
|
|
|
echo inner " Error!
|
|
|
|
|
|
|
|
The declaration must be done earlier: >
|
|
|
|
let inner: number
|
|
|
|
if cond
|
|
|
|
inner = 5
|
|
|
|
else
|
|
|
|
inner = 0
|
|
|
|
endif
|
|
|
|
echo inner
|
|
|
|
|
|
|
|
To intentionally use a variable that won't be available later, a block can be
|
|
|
|
used: >
|
|
|
|
{
|
|
|
|
let temp = 'temp'
|
|
|
|
...
|
|
|
|
}
|
|
|
|
echo temp " Error!
|
|
|
|
|
2020-02-04 22:53:05 +01:00
|
|
|
An existing variable cannot be assigned to with `:let`, since that implies a
|
2020-01-26 15:56:19 +01:00
|
|
|
declaration. An exception is global variables: these can be both used with
|
|
|
|
and without `:let`, because there is no rule about where they are declared.
|
|
|
|
|
|
|
|
Variables cannot shadow previously defined variables.
|
|
|
|
Variables may shadow Ex commands, rename the variable if needed.
|
|
|
|
|
2020-05-01 16:07:38 +02:00
|
|
|
Global variables and user defined functions must be prefixed with "g:", also
|
|
|
|
at the script level. >
|
2020-04-10 22:10:56 +02:00
|
|
|
vim9script
|
|
|
|
let script_local = 'text'
|
|
|
|
let g:global = 'value'
|
2020-05-01 16:07:38 +02:00
|
|
|
let Funcref = g:ThatFunction
|
2020-04-10 22:10:56 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
Since "&opt = value" is now assigning a value to option "opt", ":&" cannot be
|
|
|
|
used to repeat a `:substitute` command.
|
|
|
|
|
|
|
|
|
|
|
|
Omitting :call and :eval ~
|
|
|
|
|
|
|
|
Functions can be called without `:call`: >
|
|
|
|
writefile(lines, 'file')
|
2020-02-04 22:53:05 +01:00
|
|
|
Using `:call` is still possible, but this is discouraged.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
A method call without `eval` is possible, so long as the start is an
|
2020-02-22 18:36:32 +01:00
|
|
|
identifier or can't be an Ex command. It does NOT work for string constants: >
|
2020-01-26 15:56:19 +01:00
|
|
|
myList->add(123) " works
|
|
|
|
g:myList->add(123) " works
|
|
|
|
[1, 2, 3]->Process() " works
|
|
|
|
#{a: 1, b: 2}->Process() " works
|
|
|
|
{'a': 1, 'b': 2}->Process() " works
|
|
|
|
"foobar"->Process() " does NOT work
|
2020-02-22 18:36:32 +01:00
|
|
|
("foobar")->Process() " works
|
|
|
|
'foobar'->Process() " does NOT work
|
|
|
|
('foobar')->Process() " works
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-02-21 18:42:43 +01:00
|
|
|
In case there is ambiguity between a function name and an Ex command, use ":"
|
|
|
|
to make clear you want to use the Ex command. For example, there is both the
|
|
|
|
`:substitute` command and the `substitute()` function. When the line starts
|
|
|
|
with `substitute(` this will use the function, prepend a colon to use the
|
|
|
|
command instead: >
|
2020-02-22 18:36:32 +01:00
|
|
|
:substitute(pattern (replacement (
|
2020-02-21 18:42:43 +01:00
|
|
|
|
2020-02-29 22:06:30 +01:00
|
|
|
Note that while variables need to be defined before they can be used,
|
|
|
|
functions can be called before being defined. This is required to be able
|
|
|
|
have cyclic dependencies between functions. It is slightly less efficient,
|
|
|
|
since the function has to be looked up by name. And a typo in the function
|
|
|
|
name will only be found when the call is executed.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-04-10 22:10:56 +02:00
|
|
|
Omitting function() ~
|
|
|
|
|
|
|
|
A user defined function can be used as a function reference in an expression
|
|
|
|
without `function()`. The argument types and return type will then be checked.
|
|
|
|
The function must already have been defined. >
|
|
|
|
|
|
|
|
let Funcref = MyFunction
|
|
|
|
|
|
|
|
When using `function()` the resulting type is "func", a function with any
|
|
|
|
number of arguments and any return type. The function can be defined later.
|
|
|
|
|
|
|
|
|
2020-04-12 16:38:57 +02:00
|
|
|
Automatic line continuation ~
|
|
|
|
|
|
|
|
In many cases it is obvious that an expression continues on the next line. In
|
|
|
|
those cases there is no need to prefix the line with a backslash. For
|
|
|
|
example, when a list spans multiple lines: >
|
|
|
|
let mylist = [
|
|
|
|
'one',
|
|
|
|
'two',
|
|
|
|
]
|
2020-04-12 20:19:16 +02:00
|
|
|
And when a dict spans multiple lines: >
|
|
|
|
let mydict = #{
|
|
|
|
one: 1,
|
|
|
|
two: 2,
|
|
|
|
}
|
|
|
|
Function call: >
|
|
|
|
let result = Func(
|
|
|
|
arg1,
|
|
|
|
arg2
|
|
|
|
)
|
|
|
|
|
2020-04-12 20:55:20 +02:00
|
|
|
For binary operators iin expressions not in [], {} or () a line break is
|
|
|
|
possible AFTER the operators. For example: >
|
|
|
|
let text = lead ..
|
|
|
|
middle ..
|
|
|
|
end
|
|
|
|
let total = start +
|
|
|
|
end -
|
|
|
|
correction
|
|
|
|
let result = positive ?
|
|
|
|
PosFunc(arg) :
|
|
|
|
NegFunc(arg)
|
|
|
|
|
2020-04-12 20:19:16 +02:00
|
|
|
Note that "enddef" cannot be used at the start of a continuation line, it ends
|
|
|
|
the current function.
|
2020-04-12 16:38:57 +02:00
|
|
|
|
2020-04-12 21:53:00 +02:00
|
|
|
It is also possible to split a function header over multiple lines, in between
|
|
|
|
arguments: >
|
|
|
|
def MyFunc(
|
|
|
|
text: string,
|
|
|
|
separator = '-'
|
|
|
|
): string
|
|
|
|
|
2020-04-12 16:38:57 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
No curly braces expansion ~
|
|
|
|
|
|
|
|
|curly-braces-names| cannot be used.
|
|
|
|
|
|
|
|
|
2020-02-04 22:53:05 +01:00
|
|
|
No :append, :change or :insert ~
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-02-04 22:53:05 +01:00
|
|
|
These commands are too quickly confused with local variable names.
|
|
|
|
|
|
|
|
|
|
|
|
Comparators ~
|
|
|
|
|
|
|
|
The 'ignorecase' option is not used for comparators that use strings.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
White space ~
|
|
|
|
|
|
|
|
Vim9 script enforces proper use of white space. This is no longer allowed: >
|
|
|
|
let var=234 " Error!
|
|
|
|
let var= 234 " Error!
|
|
|
|
let var =234 " Error!
|
|
|
|
There must be white space before and after the "=": >
|
|
|
|
let var = 234 " OK
|
2020-04-13 14:41:35 +02:00
|
|
|
White space must also be put before the # that starts a comment: >
|
|
|
|
let var = 234# Error!
|
|
|
|
let var = 234 # OK
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
White space is required around most operators.
|
|
|
|
|
|
|
|
White space is not allowed:
|
|
|
|
- Between a function name and the "(": >
|
|
|
|
call Func (arg) " Error!
|
|
|
|
call Func
|
|
|
|
\ (arg) " Error!
|
|
|
|
call Func(arg) " OK
|
|
|
|
call Func(
|
|
|
|
\ arg) " OK
|
2020-02-21 18:42:43 +01:00
|
|
|
call Func(
|
|
|
|
\ arg " OK
|
|
|
|
\ )
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
Conditions and expressions ~
|
|
|
|
|
|
|
|
Conditions and expression are mostly working like they do in JavaScript. A
|
|
|
|
difference is made where JavaScript does not work like most people expect.
|
|
|
|
Specifically, an empty list is falsey.
|
|
|
|
|
|
|
|
Any type of variable can be used as a condition, there is no error, not even
|
|
|
|
for using a list or job. This is very much like JavaScript, but there are a
|
|
|
|
few exceptions.
|
|
|
|
|
|
|
|
type TRUE when ~
|
|
|
|
bool v:true
|
|
|
|
number non-zero
|
|
|
|
float non-zero
|
|
|
|
string non-empty
|
|
|
|
blob non-empty
|
|
|
|
list non-empty (different from JavaScript)
|
|
|
|
dictionary non-empty (different from JavaScript)
|
2020-04-10 22:10:56 +02:00
|
|
|
func when there is a function name
|
2020-01-26 15:56:19 +01:00
|
|
|
special v:true
|
|
|
|
job when not NULL
|
|
|
|
channel when not NULL
|
|
|
|
class when not NULL
|
|
|
|
object when not NULL (TODO: when isTrue() returns v:true)
|
|
|
|
|
|
|
|
The boolean operators "||" and "&&" do not change the value: >
|
|
|
|
8 || 2 == 8
|
|
|
|
0 || 2 == 2
|
|
|
|
0 || '' == ''
|
|
|
|
8 && 2 == 2
|
|
|
|
0 && 2 == 0
|
|
|
|
[] && 2 == []
|
|
|
|
|
|
|
|
When using `..` for string concatenation the arguments are always converted to
|
|
|
|
string. >
|
|
|
|
'hello ' .. 123 == 'hello 123'
|
|
|
|
'hello ' .. v:true == 'hello true'
|
|
|
|
|
|
|
|
In Vim9 script one can use "true" for v:true and "false" for v:false.
|
|
|
|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
3. New style functions *fast-functions*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
|
|
|
*:def*
|
|
|
|
:def[!] {name}([arguments])[: {return-type}
|
|
|
|
Define a new function by the name {name}. The body of
|
|
|
|
the function follows in the next lines, until the
|
|
|
|
matching `:enddef`.
|
|
|
|
|
2020-04-03 21:59:57 +02:00
|
|
|
When {return-type} is omitted or is "void" the
|
|
|
|
function is not expected to return anything.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
{arguments} is a sequence of zero or more argument
|
|
|
|
declarations. There are three forms:
|
|
|
|
{name}: {type}
|
|
|
|
{name} = {value}
|
|
|
|
{name}: {type} = {value}
|
|
|
|
The first form is a mandatory argument, the caller
|
|
|
|
must always provide them.
|
|
|
|
The second and third form are optional arguments.
|
|
|
|
When the caller omits an argument the {value} is used.
|
|
|
|
|
2020-02-04 22:53:05 +01:00
|
|
|
NOTE: It is possible to nest `:def` inside another
|
|
|
|
`:def`, but it is not possible to nest `:def` inside
|
|
|
|
`:function`, for backwards compatibility.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
[!] is used as with `:function`.
|
|
|
|
|
|
|
|
*:enddef*
|
|
|
|
:enddef End of a function defined with `:def`.
|
|
|
|
|
|
|
|
|
2020-02-21 18:42:43 +01:00
|
|
|
If the script the function is defined in is Vim9 script, then script-local
|
|
|
|
variables can be accessed without the "s:" prefix. They must be defined
|
|
|
|
before the function. If the script the function is defined in is legacy
|
|
|
|
script, then script-local variables must be accessed with the "s:" prefix.
|
|
|
|
|
|
|
|
|
2020-02-15 21:41:42 +01:00
|
|
|
*:disa* *:disassemble*
|
|
|
|
:disa[ssemble] {func} Show the instructions generated for {func}.
|
|
|
|
This is for debugging and testing.
|
2020-02-29 22:06:30 +01:00
|
|
|
Note that for command line completion of {func} you
|
|
|
|
can prepend "s:" to find script-local functions.
|
2020-02-15 21:41:42 +01:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
4. Types *vim9-types*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
|
|
|
The following builtin types are supported:
|
|
|
|
bool
|
|
|
|
number
|
|
|
|
float
|
|
|
|
string
|
|
|
|
blob
|
2020-04-03 21:59:57 +02:00
|
|
|
list<{type}>
|
|
|
|
dict<{type}>
|
2020-01-26 15:56:19 +01:00
|
|
|
job
|
|
|
|
channel
|
2020-03-14 08:19:51 +01:00
|
|
|
func
|
2020-04-10 22:10:56 +02:00
|
|
|
func: {type}
|
2020-04-03 21:59:57 +02:00
|
|
|
func({type}, ...)
|
|
|
|
func({type}, ...): {type}
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Not supported yet:
|
2020-04-03 21:59:57 +02:00
|
|
|
tuple<a: {type}, b: {type}, ...>
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-04-03 21:59:57 +02:00
|
|
|
These types can be used in declarations, but no value will have this type:
|
|
|
|
{type}|{type}
|
2020-01-26 15:56:19 +01:00
|
|
|
void
|
|
|
|
any
|
|
|
|
|
2020-04-03 21:59:57 +02:00
|
|
|
There is no array type, use list<{type}> instead. For a list constant an
|
2020-01-26 15:56:19 +01:00
|
|
|
efficient implementation is used that avoids allocating lot of small pieces of
|
|
|
|
memory.
|
|
|
|
|
2020-04-03 21:59:57 +02:00
|
|
|
A partial and function can be declared in more or less specific ways:
|
|
|
|
func any kind of function reference, no type
|
2020-04-10 22:10:56 +02:00
|
|
|
checking for arguments or return value
|
2020-04-03 21:59:57 +02:00
|
|
|
func: {type} any number and type of arguments with specific
|
|
|
|
return type
|
2020-04-10 22:10:56 +02:00
|
|
|
func({type}) function with argument type, does not return
|
2020-04-03 21:59:57 +02:00
|
|
|
a value
|
2020-04-10 22:10:56 +02:00
|
|
|
func({type}): {type} function with argument type and return type
|
|
|
|
func(?{type}) function with type of optional argument, does
|
|
|
|
not return a value
|
|
|
|
func(...{type}) function with type of variable number of
|
|
|
|
arguments, does not return a value
|
|
|
|
func({type}, ?{type}, ...{type}): {type}
|
|
|
|
function with:
|
|
|
|
- type of mandatory argument
|
|
|
|
- type of optional argument
|
|
|
|
- type of variable number of arguments
|
|
|
|
- return type
|
2020-04-03 21:59:57 +02:00
|
|
|
|
|
|
|
If the return type is "void" the function does not return a value.
|
|
|
|
|
|
|
|
The reference can also be a |Partial|, in which case it stores extra arguments
|
|
|
|
and/or a dictionary, which are not visible to the caller. Since they are
|
|
|
|
called in the same way the declaration is the same.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Custom types can be defined with `:type`: >
|
|
|
|
:type MyList list<string>
|
|
|
|
{not implemented yet}
|
|
|
|
|
|
|
|
And classes and interfaces can be used as types: >
|
|
|
|
:class MyClass
|
|
|
|
:let mine: MyClass
|
|
|
|
|
|
|
|
:interface MyInterface
|
|
|
|
:let mine: MyInterface
|
|
|
|
|
|
|
|
:class MyTemplate<Targ>
|
|
|
|
:let mine: MyTemplate<number>
|
|
|
|
:let mine: MyTemplate<string>
|
|
|
|
|
|
|
|
:class MyInterface<Targ>
|
|
|
|
:let mine: MyInterface<number>
|
|
|
|
:let mine: MyInterface<string>
|
|
|
|
{not implemented yet}
|
|
|
|
|
|
|
|
|
|
|
|
Type inference *type-inference*
|
|
|
|
|
|
|
|
In general: Whenever the type is clear it can be omitted. For example, when
|
|
|
|
declaring a variable and giving it a value: >
|
|
|
|
let var = 0 " infers number type
|
|
|
|
let var = 'hello' " infers string type
|
|
|
|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
5. Namespace, Import and Export
|
|
|
|
*vim9script* *vim9-export* *vim9-import*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
|
|
|
A Vim9 script can be written to be imported. This means that everything in
|
|
|
|
the script is local, unless exported. Those exported items, and only those
|
|
|
|
items, can then be imported in another script.
|
|
|
|
|
|
|
|
|
|
|
|
Namespace ~
|
|
|
|
*:vim9script* *:vim9*
|
2020-02-04 22:53:05 +01:00
|
|
|
To recognize a file that can be imported the `vim9script` statement must
|
2020-01-26 15:56:19 +01:00
|
|
|
appear as the first statement in the file. It tells Vim to interpret the
|
|
|
|
script in its own namespace, instead of the global namespace. If a file
|
|
|
|
starts with: >
|
|
|
|
vim9script
|
|
|
|
let myvar = 'yes'
|
|
|
|
Then "myvar" will only exist in this file. While without `vim9script` it would
|
|
|
|
be available as `g:myvar` from any other script and function.
|
|
|
|
|
|
|
|
The variables at the file level are very much like the script-local "s:"
|
2020-04-20 19:52:53 +02:00
|
|
|
variables in legacy Vim script, but the "s:" is omitted. And they cannot be
|
|
|
|
deleted.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-04-20 19:52:53 +02:00
|
|
|
In Vim9 script the global "g:" namespace can still be used as before. And the
|
|
|
|
"w:", "b:" and "t:" namespaces. These have in common that variables are not
|
|
|
|
declared and they can be deleted.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
A side effect of `:vim9script` is that the 'cpoptions' option is set to the
|
|
|
|
Vim default value, like with: >
|
|
|
|
:set cpo&vim
|
|
|
|
One of the effects is that |line-continuation| is always enabled.
|
|
|
|
The original value of 'cpoptions' is restored at the end of the script.
|
|
|
|
|
|
|
|
|
|
|
|
Export ~
|
|
|
|
*:export* *:exp*
|
|
|
|
Exporting one item can be written as: >
|
|
|
|
export const EXPORTED_CONST = 1234
|
|
|
|
export let someValue = ...
|
|
|
|
export def MyFunc() ...
|
|
|
|
export class MyClass ...
|
|
|
|
|
|
|
|
As this suggests, only constants, variables, `:def` functions and classes can
|
|
|
|
be exported.
|
|
|
|
|
|
|
|
Alternatively, an export statement can be used to export several already
|
|
|
|
defined (otherwise script-local) items: >
|
|
|
|
export {EXPORTED_CONST, someValue, MyFunc, MyClass}
|
|
|
|
|
|
|
|
|
|
|
|
Import ~
|
|
|
|
*:import* *:imp*
|
|
|
|
The exported items can be imported individually in another Vim9 script: >
|
|
|
|
import EXPORTED_CONST from "thatscript.vim"
|
|
|
|
import MyClass from "myclass.vim"
|
|
|
|
|
|
|
|
To import multiple items at the same time: >
|
|
|
|
import {someValue, MyClass} from "thatscript.vim"
|
|
|
|
|
2020-02-04 22:53:05 +01:00
|
|
|
In case the name is ambiguous, another name can be specified: >
|
2020-01-26 15:56:19 +01:00
|
|
|
import MyClass as ThatClass from "myclass.vim"
|
|
|
|
import {someValue, MyClass as ThatClass} from "myclass.vim"
|
|
|
|
|
|
|
|
To import all exported items under a specific identifier: >
|
|
|
|
import * as That from 'thatscript.vim'
|
|
|
|
|
|
|
|
Then you can use "That.EXPORTED_CONST", "That.someValue", etc. You are free
|
|
|
|
to choose the name "That", but it is highly recommended to use the name of the
|
|
|
|
script file to avoid confusion.
|
|
|
|
|
|
|
|
The script name after `import` can be:
|
|
|
|
- A relative path, starting "." or "..". This finds a file relative to the
|
|
|
|
location of the script file itself. This is useful to split up a large
|
|
|
|
plugin into several files.
|
|
|
|
- An absolute path, starting with "/" on Unix or "D:/" on MS-Windows. This
|
|
|
|
will be rarely used.
|
|
|
|
- A path not being relative or absolute. This will be found in the
|
|
|
|
"import" subdirectories of 'runtimepath' entries. The name will usually be
|
|
|
|
longer and unique, to avoid loading the wrong file.
|
|
|
|
|
|
|
|
Once a vim9 script file has been imported, the result is cached and used the
|
|
|
|
next time the same script is imported. It will not be read again.
|
|
|
|
*:import-cycle*
|
|
|
|
The `import` commands are executed when encountered. If that script (directly
|
|
|
|
or indirectly) imports the current script, then items defined after the
|
|
|
|
`import` won't be processed yet. Therefore cyclic imports can exist, but may
|
|
|
|
result in undefined items.
|
|
|
|
|
|
|
|
|
|
|
|
Import in an autoload script ~
|
|
|
|
|
|
|
|
For optimal startup speed, loading scripts should be postponed until they are
|
2020-02-04 22:53:05 +01:00
|
|
|
actually needed. A recommended mechanism:
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
1. In the plugin define user commands, functions and/or mappings that refer to
|
|
|
|
an autoload script. >
|
|
|
|
command -nargs=1 SearchForStuff call searchfor#Stuff(<f-args>)
|
|
|
|
|
|
|
|
< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen.
|
|
|
|
|
|
|
|
2. In the autocommand script do the actual work. You can import items from
|
|
|
|
other files to split up functionality in appropriate pieces. >
|
|
|
|
vim9script
|
|
|
|
import FilterFunc from "../import/someother.vim"
|
|
|
|
def searchfor#Stuff(arg: string)
|
|
|
|
let filtered = FilterFunc(arg)
|
|
|
|
...
|
|
|
|
< This goes in .../autoload/searchfor.vim. "searchfor" in the file name
|
|
|
|
must be exactly the same as the prefix for the function name, that is how
|
|
|
|
Vim finds the file.
|
|
|
|
|
|
|
|
3. Other functionality, possibly shared between plugins, contains the exported
|
|
|
|
items and any private items. >
|
|
|
|
vim9script
|
|
|
|
let localVar = 'local'
|
|
|
|
export def FilterFunc(arg: string): string
|
|
|
|
...
|
|
|
|
< This goes in .../import/someother.vim.
|
|
|
|
|
|
|
|
|
|
|
|
Import in legacy Vim script ~
|
|
|
|
|
|
|
|
If an `import` statement is used in legacy Vim script, for identifier the
|
|
|
|
script-local "s:" namespace will be used, even when "s:" is not specified.
|
|
|
|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
9. Rationale *vim9-rationale*
|
|
|
|
|
|
|
|
The :def command ~
|
|
|
|
|
|
|
|
Plugin writers have asked for a much faster Vim script. Investigation have
|
2020-02-04 22:53:05 +01:00
|
|
|
shown that keeping the existing semantics of function calls make this close to
|
2020-01-26 15:56:19 +01:00
|
|
|
impossible, because of the overhead involved with calling a function, setting
|
|
|
|
up the local function scope and executing lines. There are many details that
|
|
|
|
need to be handled, such as error messages and exceptions. The need to create
|
|
|
|
a dictionary for a: and l: scopes, the a:000 list and several others add too
|
|
|
|
much overhead that cannot be avoided.
|
|
|
|
|
|
|
|
Therefore the `:def` method to define a new-style function had to be added,
|
|
|
|
which allows for a function with different semantics. Most things still work
|
|
|
|
as before, but some parts do not. A new way to define a function was
|
|
|
|
considered the best way to separate the old-style code from Vim9 script code.
|
|
|
|
|
|
|
|
Using "def" to define a function comes from Python. Other languages use
|
|
|
|
"function" which clashes with legacy Vim script.
|
|
|
|
|
|
|
|
|
|
|
|
Type checking ~
|
|
|
|
|
|
|
|
When compiling lines of Vim commands into instructions as much as possible
|
|
|
|
should be done at compile time. Postponing it to runtime makes the execution
|
|
|
|
slower and means mistakes are found only later. For example, when
|
|
|
|
encountering the "+" character and compiling this into a generic add
|
|
|
|
instruction, at execution time the instruction would have to inspect the type
|
|
|
|
of the arguments and decide what kind of addition to do. And when the
|
|
|
|
type is dictionary throw an error. If the types are known to be numbers then
|
|
|
|
an "add number" instruction can be used, which is faster. The error can be
|
|
|
|
given at compile time, no error handling is needed at runtime.
|
|
|
|
|
|
|
|
The syntax for types is similar to Java, since it is easy to understand and
|
|
|
|
widely used. The type names are what was used in Vim before, with some
|
|
|
|
additions such as "void" and "bool".
|
|
|
|
|
|
|
|
|
|
|
|
JavaScript/TypeScript syntax and semantics ~
|
|
|
|
|
|
|
|
Script writers have complained that the Vim script syntax is unexpectedly
|
|
|
|
different from what they are used to. To reduce this complaint popular
|
|
|
|
languages will be used as an example. At the same time, we do not want to
|
2020-02-04 22:53:05 +01:00
|
|
|
abandon the well-known parts of legacy Vim script.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Since Vim already uses `:let` and `:const` and optional type checking is
|
|
|
|
desirable, the JavaScript/TypeScript syntax fits best for variable
|
|
|
|
declarations. >
|
|
|
|
const greeting = 'hello' " string type is inferred
|
|
|
|
let name: string
|
|
|
|
...
|
|
|
|
name = 'John'
|
|
|
|
|
|
|
|
Expression evaluation was already close to what JavaScript and other languages
|
|
|
|
are doing. Some details are unexpected and can be fixed. For example how the
|
|
|
|
|| and && operators work. Legacy Vim script: >
|
|
|
|
let result = 44
|
|
|
|
...
|
|
|
|
return result || 0 " returns 1
|
|
|
|
|
|
|
|
Vim9 script works like JavaScript, keep the value: >
|
|
|
|
let result = 44
|
|
|
|
...
|
|
|
|
return result || 0 " returns 44
|
|
|
|
|
|
|
|
On the other hand, overloading "+" to use both for addition and string
|
|
|
|
concatenation goes against legacy Vim script and often leads to mistakes.
|
|
|
|
For that reason we will keep using ".." for string concatenation. Lua also
|
|
|
|
uses ".." this way.
|
|
|
|
|
|
|
|
|
|
|
|
Import and Export ~
|
|
|
|
|
|
|
|
A problem of legacy Vim script is that by default all functions and variables
|
|
|
|
are global. It is possible to make them script-local, but then they are not
|
|
|
|
available in other scripts.
|
|
|
|
|
|
|
|
In Vim9 script a mechanism very similar to the Javascript import and export
|
|
|
|
mechanism is supported. It is a variant to the existing `:source` command
|
|
|
|
that works like one would expect:
|
|
|
|
- Instead of making everything global by default, everything is script-local,
|
|
|
|
unless exported.
|
|
|
|
- When importing a script the symbols that are imported are listed, avoiding
|
|
|
|
name conflicts and failures if later functionality is added.
|
|
|
|
- The mechanism allows for writing a big, long script with a very clear API:
|
|
|
|
the exported function(s) and class(es).
|
|
|
|
- By using relative paths loading can be much faster for an import inside of a
|
|
|
|
package, no need to search many directories.
|
|
|
|
- Once an import has been used, it can be cached and loading it again can be
|
|
|
|
avoided.
|
|
|
|
- The Vim-specific use of "s:" to make things script-local can be dropped.
|
|
|
|
|
|
|
|
|
|
|
|
Classes ~
|
|
|
|
|
|
|
|
Vim supports interfaces to Perl, Python, Lua, Tcl and a few others. But
|
|
|
|
these have never become widespread. When Vim 9 was designed a decision was
|
|
|
|
made to phase out these interfaces and concentrate on Vim script, while
|
|
|
|
encouraging plugin authors to write code in any language and run it as an
|
|
|
|
external tool, using jobs and channels.
|
|
|
|
|
|
|
|
Still, using an external tool has disadvantages. An alternative is to convert
|
|
|
|
the tool into Vim script. For that to be possible without too much
|
|
|
|
translation, and keeping the code fast at the same time, the constructs of the
|
|
|
|
tool need to be supported. Since most languages support classes the lack of
|
|
|
|
class support in Vim is then a problem.
|
|
|
|
|
|
|
|
Previously Vim supported a kind-of object oriented programming by adding
|
|
|
|
methods to a dictionary. With some care this could be made to work, but it
|
|
|
|
does not look like real classes. On top of that, it's very slow, because of
|
|
|
|
the use of dictionaries.
|
|
|
|
|
|
|
|
The support of classes in Vim9 script is a "minimal common functionality" of
|
|
|
|
class support in most languages. It works mostly like Java, which is the most
|
|
|
|
popular programming language.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vim:tw=78:ts=8:noet:ft=help:norl:
|