2020-12-24 15:14:01 +01:00
|
|
|
*vim9.txt* For Vim version 8.2. Last change: 2020 Dec 24
|
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-12-28 20:53:21 +01:00
|
|
|
Vim9 script commands and expressions. *Vim9* *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
|
|
|
|
|
|
|
|
|
2020-08-07 19:54:59 +02:00
|
|
|
1. What is Vim9 script? |vim9-script|
|
2020-01-26 15:56:19 +01:00
|
|
|
2. Differences |vim9-differences|
|
|
|
|
3. New style functions |fast-functions|
|
|
|
|
4. Types |vim9-types|
|
|
|
|
5. Namespace, Import and Export |vim9script|
|
2020-09-19 18:50:13 +02:00
|
|
|
6. Future work: classes |vim9-classes|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
9. Rationale |vim9-rationale|
|
|
|
|
|
|
|
|
==============================================================================
|
|
|
|
|
2020-12-26 15:39:31 +01:00
|
|
|
1. What is Vim9 script? *Vim9-script*
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
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
|
2020-06-21 22:12:03 +02:00
|
|
|
and compatibility with Vi restricts possible solutions. Execution is quite
|
2020-05-01 16:07:38 +02:00
|
|
|
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
|
2020-05-26 21:20:45 +02:00
|
|
|
compatible. For example, making function arguments available in the
|
|
|
|
"a:" dictionary adds quite a lot of overhead. In a Vim9 function this
|
|
|
|
dictionary is not available. 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`
|
2020-09-19 18:50:13 +02:00
|
|
|
- an autocommand defined in the context of the above
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
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
|
2020-09-19 18:50:13 +02:00
|
|
|
rewrite old scripts, they keep working as before. You may want to use a few
|
|
|
|
`:def` functions for code that needs to be fast.
|
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-09-28 21:48:16 +02:00
|
|
|
Overview ~
|
|
|
|
|
|
|
|
Brief summary of the differences you will most often encounter when using Vim9
|
|
|
|
script and `:def` functions; details are below:
|
|
|
|
- Comments start with #, not ": >
|
|
|
|
echo "hello" # comment
|
|
|
|
- Using a backslash for line continuation is hardly ever needed: >
|
|
|
|
echo "hello "
|
|
|
|
.. yourName
|
|
|
|
.. ", how are you?"
|
|
|
|
- White space is required in many places.
|
|
|
|
- Assign values without `:let`, declare variables with `:var`: >
|
|
|
|
var count = 0
|
|
|
|
count += 3
|
|
|
|
- Constants can be declared with `:final` and `:const`: >
|
|
|
|
final matches = [] # add matches
|
|
|
|
const names = ['Betty', 'Peter'] # cannot be changed
|
|
|
|
- `:final` cannot be used as an abbreviation of `:finally`.
|
|
|
|
- Variables and functions are script-local by default.
|
|
|
|
- Functions are declared with argument types and return type: >
|
|
|
|
def CallMe(count: number, message: string): bool
|
|
|
|
- Call functions without `:call`: >
|
|
|
|
writefile(['done'], 'file.txt')
|
|
|
|
- You cannot use `:xit`, `:t`, `:append`, `:change`, `:insert` or curly-braces
|
|
|
|
names.
|
|
|
|
- A range before a command must be prefixed with a colon: >
|
|
|
|
:%s/this/that
|
|
|
|
|
|
|
|
|
2020-04-13 14:41:35 +02:00
|
|
|
Comments starting with # ~
|
|
|
|
|
2020-07-17 20:36:00 +02:00
|
|
|
In legacy Vim script comments start with double quote. In Vim9 script
|
|
|
|
comments start with #. >
|
|
|
|
# declarations
|
2020-09-26 15:09:30 +02:00
|
|
|
var count = 0 # number of occurrences
|
2020-04-13 14:41:35 +02:00
|
|
|
|
2020-07-17 20:36:00 +02:00
|
|
|
The reason is that a double quote can also be the start of a string. In many
|
2020-08-15 18:55:18 +02:00
|
|
|
places, especially halfway through an expression with a line break, it's hard
|
|
|
|
to tell what the meaning is, since both a string and a comment can be followed
|
|
|
|
by arbitrary text. To avoid confusion only # comments are recognized. This
|
|
|
|
is the same as in shell scripts and Python programs.
|
2020-07-17 20:36:00 +02:00
|
|
|
|
|
|
|
In Vi # is a command to list text with numbers. In Vim9 script you can use
|
|
|
|
`:number` for that. >
|
2020-07-28 20:07:27 +02:00
|
|
|
101 number
|
2020-07-17 20:36:00 +02:00
|
|
|
|
|
|
|
To improve readability there must be a space between a command and the #
|
2020-12-26 15:39:31 +01:00
|
|
|
that starts a comment: >
|
2020-12-28 20:53:21 +01:00
|
|
|
var name = value # comment
|
|
|
|
var name = value# error!
|
2020-12-26 15:39:31 +01:00
|
|
|
|
2020-12-28 20:53:21 +01:00
|
|
|
In legacy Vim script # is also used for the alternate file name. In Vim9
|
|
|
|
script you need to use %% instead. Instead of ## use %%% (stands for all
|
|
|
|
arguments).
|
2020-04-20 19:52:53 +02:00
|
|
|
|
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.
|
|
|
|
|
2020-05-26 21:20:45 +02:00
|
|
|
Many errors are already found when compiling, before the function is executed.
|
2020-05-01 16:07:38 +02:00
|
|
|
The syntax is strict, to enforce code that is easy to read and understand.
|
|
|
|
|
2020-12-10 21:11:27 +01:00
|
|
|
Compilation is done when either of these is encountered:
|
|
|
|
- the first time the function is called
|
2020-08-30 17:20:20 +02:00
|
|
|
- when the `:defcompile` command is encountered in the script where the
|
|
|
|
function was defined
|
|
|
|
- `:disassemble` is used for the function.
|
|
|
|
- a function that is compiled calls the function or uses it as a function
|
|
|
|
reference
|
2020-05-26 21:20:45 +02:00
|
|
|
|
|
|
|
`:def` has no options like `:function` does: "range", "abort", "dict" or
|
2020-12-10 21:11:27 +01:00
|
|
|
"closure". A `:def` function always aborts on an error (unless `:silent!` was
|
|
|
|
used for the command or inside a `:try` block), does not get a range passed
|
2020-12-23 13:56:35 +01:00
|
|
|
cannot be a "dict" function, and can always be a closure.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
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.
|
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
Arguments are accessed by name, without "a:", just like any other language.
|
|
|
|
There is no "a:" dictionary or "a:000" list.
|
2020-05-01 16:07:38 +02:00
|
|
|
|
|
|
|
Variable arguments are defined as the last argument, with a name and have a
|
2020-08-15 18:55:18 +02:00
|
|
|
list type, similar to TypeScript. For example, a list of numbers: >
|
2020-09-07 22:18:52 +02:00
|
|
|
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-06-14 17:29:55 +02:00
|
|
|
*vim9-scopes*
|
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-10-03 22:52:39 +02:00
|
|
|
prefixed. Using the "s:" prefix is optional. To define a global function or
|
|
|
|
variable the "g:" prefix must be used. For functions in an autoload script
|
|
|
|
the "name#" prefix is sufficient. >
|
2020-07-29 22:11:05 +02:00
|
|
|
def ThisFunction() # script-local
|
|
|
|
def s:ThisFunction() # script-local
|
2020-09-07 22:18:52 +02:00
|
|
|
def g:ThatFunction() # global
|
2020-07-29 22:11:05 +02:00
|
|
|
def scriptname#function() # autoload
|
2020-04-20 19:52:53 +02:00
|
|
|
|
2020-10-03 22:52:39 +02:00
|
|
|
When using `:function` or `:def` to specify a nested function inside a `:def`
|
|
|
|
function, this nested function is local to the code block it is defined in.
|
2020-10-11 13:57:40 +02:00
|
|
|
In a `:def` function it is not possible to define a script-local function. It
|
2020-10-03 22:52:39 +02:00
|
|
|
is possible to define a global function by 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
|
2020-10-04 16:06:05 +02:00
|
|
|
search for the function:
|
2020-10-11 13:57:40 +02:00
|
|
|
- in the function scope, in block scopes
|
2020-10-04 16:06:05 +02:00
|
|
|
- in the script scope, possibly imported
|
|
|
|
- in the list of global functions
|
|
|
|
However, it is recommended to always use "g:" to refer to a global function
|
|
|
|
for clarity.
|
|
|
|
|
|
|
|
In all cases the function must be defined before used. That is when it is
|
2020-10-26 21:12:46 +01:00
|
|
|
called, when `:defcompile` causes it to be compiled, or when code that calls
|
|
|
|
it is being compiled (to figure out the return type).
|
2020-05-01 16:07:38 +02:00
|
|
|
|
2020-08-07 19:54:59 +02:00
|
|
|
The result is that functions and variables without a namespace can usually be
|
2020-05-01 16:07:38 +02:00
|
|
|
found in the script, either defined there or imported. Global functions and
|
2020-08-07 19:54:59 +02:00
|
|
|
variables could be defined anywhere (good luck finding out where!).
|
2020-04-20 19:52:53 +02:00
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
Global functions can still be defined and deleted at nearly any time. In
|
2020-05-07 18:56:00 +02:00
|
|
|
Vim9 script script-local functions are defined once when the script is sourced
|
2020-05-26 21:20:45 +02:00
|
|
|
and cannot be deleted or replaced.
|
2020-04-20 19:52:53 +02:00
|
|
|
|
2020-12-23 13:56:35 +01:00
|
|
|
When compiling a function and a function call is encountered for a function
|
|
|
|
that is not (yet) defined, the |FuncUndefined| autocommand is not triggered.
|
|
|
|
You can use an autoload function if needed, or call a legacy function and have
|
|
|
|
|FuncUndefined| triggered there.
|
|
|
|
|
2020-04-20 19:52:53 +02:00
|
|
|
|
2020-12-26 15:39:31 +01:00
|
|
|
Reloading a Vim9 script clears functions and variables by default ~
|
|
|
|
*vim9-reload*
|
|
|
|
When loading a legacy Vim script a second time nothing is removed, the
|
|
|
|
commands will replace existing variables and functions and create new ones.
|
|
|
|
|
|
|
|
When loading a Vim9 script a second time all existing script-local functions
|
|
|
|
and variables are deleted, thus you start with a clean slate. This is useful
|
|
|
|
if you are developing a plugin and want to try a new version. If you renamed
|
|
|
|
something you don't have to worry about the old name still hanging around.
|
|
|
|
|
|
|
|
If you do want to keep items, use: >
|
2020-12-28 20:53:21 +01:00
|
|
|
vim9script noclear
|
2020-12-26 15:39:31 +01:00
|
|
|
|
|
|
|
You want to use this in scripts that use a `finish` command to bail out at
|
|
|
|
some point when loaded again. E.g. when a buffer local option is set: >
|
2020-12-28 20:53:21 +01:00
|
|
|
vim9script noclear
|
2020-12-26 15:39:31 +01:00
|
|
|
setlocal completefunc=SomeFunc
|
2020-12-28 20:53:21 +01:00
|
|
|
if exists('*g:SomeFunc') | finish | endif
|
2020-12-26 15:39:31 +01:00
|
|
|
def g:SomeFunc()
|
|
|
|
....
|
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Variable declarations with :var, :final and :const ~
|
2020-10-03 22:52:39 +02:00
|
|
|
*vim9-declaration* *:var*
|
2020-09-26 15:09:30 +02:00
|
|
|
Local variables need to be declared with `:var`. Local constants need to be
|
|
|
|
declared with `:final` or `:const`. We refer to both as "variables" in this
|
|
|
|
section.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Variables can be local to a script, function or code block: >
|
|
|
|
vim9script
|
2020-09-26 15:09:30 +02:00
|
|
|
var script_var = 123
|
2020-01-26 15:56:19 +01:00
|
|
|
def SomeFunc()
|
2020-09-26 15:09:30 +02:00
|
|
|
var func_var = script_var
|
2020-01-26 15:56:19 +01:00
|
|
|
if cond
|
2020-09-26 15:09:30 +02:00
|
|
|
var block_var = func_var
|
2020-01-26 15:56:19 +01:00
|
|
|
...
|
|
|
|
|
|
|
|
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
|
2020-09-26 15:09:30 +02:00
|
|
|
var inner = 5
|
2020-01-26 15:56:19 +01:00
|
|
|
else
|
2020-09-26 15:09:30 +02:00
|
|
|
var inner = 0
|
2020-01-26 15:56:19 +01:00
|
|
|
endif
|
2020-09-07 22:18:52 +02:00
|
|
|
echo inner # Error!
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
The declaration must be done earlier: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var inner: number
|
2020-01-26 15:56:19 +01:00
|
|
|
if cond
|
|
|
|
inner = 5
|
|
|
|
else
|
|
|
|
inner = 0
|
|
|
|
endif
|
|
|
|
echo inner
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
To intentionally hide a variable from code that follows, a block can be
|
|
|
|
used: >
|
2020-01-26 15:56:19 +01:00
|
|
|
{
|
2020-09-26 15:09:30 +02:00
|
|
|
var temp = 'temp'
|
2020-01-26 15:56:19 +01:00
|
|
|
...
|
|
|
|
}
|
2020-09-07 22:18:52 +02:00
|
|
|
echo temp # Error!
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-14 21:39:44 +02:00
|
|
|
Declaring a variable with a type but without an initializer will initialize to
|
|
|
|
zero, false or empty.
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
In Vim9 script `:let` cannot be used. An existing variable is assigned to
|
|
|
|
without any command. The same for global, window, tab, buffer and Vim
|
|
|
|
variables, because they are not really declared. They can also be deleted
|
2020-08-01 17:00:03 +02:00
|
|
|
with `:unlet`.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-07 19:54:59 +02:00
|
|
|
Variables and functions cannot shadow previously defined or imported variables
|
|
|
|
and functions.
|
2020-01-26 15:56:19 +01:00
|
|
|
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
|
2020-09-26 15:09:30 +02:00
|
|
|
var script_local = 'text'
|
2020-07-26 17:00:44 +02:00
|
|
|
g:global = 'value'
|
2020-09-26 15:09:30 +02:00
|
|
|
var Funcref = g:ThatFunction
|
2020-04-10 22:10:56 +02:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Since `&opt = value` is now assigning a value to option "opt", ":&" cannot be
|
2020-01-26 15:56:19 +01:00
|
|
|
used to repeat a `:substitute` command.
|
2020-09-14 21:39:44 +02:00
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Constants ~
|
|
|
|
*vim9-const* *vim9-final*
|
|
|
|
How constants work varies between languages. Some consider a variable that
|
|
|
|
can't be assigned another value a constant. JavaScript is an example. Others
|
|
|
|
also make the value immutable, thus when a constant uses a list, the list
|
|
|
|
cannot be changed. In Vim9 we can use both.
|
|
|
|
|
|
|
|
`:const` is used for making both the variable and the value a constant. Use
|
|
|
|
this for composite structures that you want to make sure will not be modified.
|
|
|
|
Example: >
|
|
|
|
const myList = [1, 2]
|
|
|
|
myList = [3, 4] # Error!
|
|
|
|
myList[0] = 9 # Error!
|
|
|
|
muList->add(3) # Error!
|
2020-10-03 22:52:39 +02:00
|
|
|
< *:final*
|
2020-09-26 15:09:30 +02:00
|
|
|
`:final` is used for making only the variable a constant, the value can be
|
|
|
|
changed. This is well known from Java. Example: >
|
|
|
|
final myList = [1, 2]
|
|
|
|
myList = [3, 4] # Error!
|
|
|
|
myList[0] = 9 # OK
|
|
|
|
muList->add(3) # OK
|
2020-09-14 21:39:44 +02:00
|
|
|
|
|
|
|
It is common to write constants as ALL_CAPS, but you don't have to.
|
|
|
|
|
|
|
|
The constant only applies to the value itself, not what it refers to. >
|
2020-09-26 15:09:30 +02:00
|
|
|
final females = ["Mary"]
|
|
|
|
const NAMES = [["John", "Peter"], females]
|
2020-09-14 21:39:44 +02:00
|
|
|
NAMES[0] = ["Jack"] # Error!
|
2020-09-26 15:09:30 +02:00
|
|
|
NAMES[0][0] = "Jack" # Error!
|
2020-09-14 21:39:44 +02:00
|
|
|
NAMES[1] = ["Emma"] # Error!
|
|
|
|
Names[1][0] = "Emma" # OK, now females[0] == "Emma"
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
< *E1092*
|
2020-07-26 17:00:44 +02:00
|
|
|
Declaring more than one variable at a time, using the unpack notation, is
|
|
|
|
currently not supported: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var [v1, v2] = GetValues() # Error!
|
2020-07-26 17:00:44 +02:00
|
|
|
That is because the type needs to be inferred from the list item type, which
|
|
|
|
isn't that easy.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Omitting :call and :eval ~
|
|
|
|
|
|
|
|
Functions can be called without `:call`: >
|
2020-09-07 22:18:52 +02:00
|
|
|
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-07-28 20:07:27 +02:00
|
|
|
identifier or can't be an Ex command. Examples: >
|
|
|
|
myList->add(123)
|
|
|
|
g:myList->add(123)
|
|
|
|
[1, 2, 3]->Process()
|
2020-11-19 18:53:18 +01:00
|
|
|
{a: 1, b: 2}->Process()
|
2020-07-28 20:07:27 +02:00
|
|
|
"foobar"->Process()
|
|
|
|
("foobar")->Process()
|
|
|
|
'foobar'->Process()
|
|
|
|
('foobar')->Process()
|
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
In the rare case there is ambiguity between a function name and an Ex command,
|
2020-08-07 19:54:59 +02:00
|
|
|
prepend ":" 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,
|
2020-08-15 18:55:18 +02:00
|
|
|
functions can be called before being defined. This is required to allow
|
|
|
|
for cyclic dependencies between functions. It is slightly less efficient,
|
2020-02-29 22:06:30 +01:00
|
|
|
since the function has to be looked up by name. And a typo in the function
|
2020-07-28 20:07:27 +02:00
|
|
|
name will only be found when the function is called.
|
2020-02-29 22:06:30 +01:00
|
|
|
|
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. >
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
var Funcref = MyFunction
|
2020-04-10 22:10:56 +02:00
|
|
|
|
|
|
|
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-12-26 15:39:31 +01:00
|
|
|
Lambda using => instead of -> ~
|
2020-12-24 15:14:01 +01:00
|
|
|
|
|
|
|
In legacy script there can be confusion between using "->" for a method call
|
|
|
|
and for a lambda. Also, when a "{" is found the parser needs to figure out if
|
|
|
|
it is the start of a lambda or a dictionary, which is now more complicated
|
|
|
|
because of the use of argument types.
|
|
|
|
|
|
|
|
To avoid these problems Vim9 script uses a different syntax for a lambda,
|
|
|
|
which is similar to Javascript: >
|
|
|
|
var Lambda = (arg) => expression
|
|
|
|
|
2020-12-26 15:39:31 +01:00
|
|
|
No line break is allowed in the arguments of a lambda up to and including the
|
2020-12-24 15:14:01 +01:00
|
|
|
"=>". This is OK: >
|
|
|
|
filter(list, (k, v) =>
|
|
|
|
v > 0)
|
|
|
|
This does not work: >
|
|
|
|
filter(list, (k, v)
|
|
|
|
=> v > 0)
|
2020-12-28 20:53:21 +01:00
|
|
|
This also does not work: >
|
2020-12-24 15:14:01 +01:00
|
|
|
filter(list, (k,
|
|
|
|
v) => v > 0)
|
2020-12-28 20:53:21 +01:00
|
|
|
But you can use a backslash to concatenate the lines before parsing: >
|
|
|
|
filter(list, (k,
|
|
|
|
\ v)
|
|
|
|
\ => v > 0)
|
2020-12-24 15:14:01 +01:00
|
|
|
|
|
|
|
Additionally, a lambda can contain statements in {}: >
|
|
|
|
var Lambda = (arg) => {
|
|
|
|
g:was_called = 'yes'
|
|
|
|
return expression
|
|
|
|
}
|
|
|
|
NOT IMPLEMENTED YET
|
|
|
|
|
2020-12-26 15:39:31 +01:00
|
|
|
To avoid the "{" of a dictionary literal to be recognized as a statement block
|
|
|
|
wrap it in parenthesis: >
|
|
|
|
var Lambda = (arg) => ({key: 42})
|
2020-12-24 15:14:01 +01:00
|
|
|
|
|
|
|
|
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
|
2020-12-28 20:53:21 +01:00
|
|
|
those cases there is no need to prefix the line with a backslash (see
|
|
|
|
|line-continuation|). For example, when a list spans multiple lines: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var mylist = [
|
2020-04-12 16:38:57 +02:00
|
|
|
'one',
|
|
|
|
'two',
|
|
|
|
]
|
2020-04-12 20:19:16 +02:00
|
|
|
And when a dict spans multiple lines: >
|
2020-11-19 18:53:18 +01:00
|
|
|
var mydict = {
|
2020-04-12 20:19:16 +02:00
|
|
|
one: 1,
|
|
|
|
two: 2,
|
|
|
|
}
|
|
|
|
Function call: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = Func(
|
2020-04-12 20:19:16 +02:00
|
|
|
arg1,
|
|
|
|
arg2
|
|
|
|
)
|
|
|
|
|
2020-06-22 23:02:51 +02:00
|
|
|
For binary operators in expressions not in [], {} or () a line break is
|
|
|
|
possible just before or after the operator. For example: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var text = lead
|
2020-06-22 23:02:51 +02:00
|
|
|
.. middle
|
|
|
|
.. end
|
2020-09-26 15:09:30 +02:00
|
|
|
var total = start +
|
2020-04-12 20:55:20 +02:00
|
|
|
end -
|
|
|
|
correction
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = positive
|
2020-06-22 23:02:51 +02:00
|
|
|
? PosFunc(arg)
|
|
|
|
: NegFunc(arg)
|
2020-04-12 20:55:20 +02:00
|
|
|
|
2020-07-26 17:00:44 +02:00
|
|
|
For a method call using "->" and a member using a dot, a line break is allowed
|
|
|
|
before it: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = GetBuilder()
|
2020-06-21 22:12:03 +02:00
|
|
|
->BuilderSetWidth(333)
|
|
|
|
->BuilderSetHeight(777)
|
|
|
|
->BuilderBuild()
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = MyDict
|
2020-07-26 17:00:44 +02:00
|
|
|
.member
|
2020-06-21 22:12:03 +02:00
|
|
|
|
2020-12-28 20:53:21 +01:00
|
|
|
For commands that have an argument that is a list of commands, the | character
|
|
|
|
at the start of the line indicates line continuation: >
|
|
|
|
autocmd BufNewFile *.match if condition
|
|
|
|
| echo 'match'
|
|
|
|
| endif
|
|
|
|
|
2020-06-22 23:02:51 +02:00
|
|
|
< *E1050*
|
|
|
|
To make it possible for the operator at the start of the line to be
|
2020-07-10 22:00:53 +02:00
|
|
|
recognized, it is required to put a colon before a range. This will add
|
2020-06-22 23:02:51 +02:00
|
|
|
"start" and print: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = start
|
2020-06-22 23:02:51 +02:00
|
|
|
+ print
|
2020-07-10 22:00:53 +02:00
|
|
|
Like this: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = start + print
|
2020-07-10 22:00:53 +02:00
|
|
|
|
2020-06-22 23:02:51 +02:00
|
|
|
This will assign "start" and print a line: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var result = start
|
2020-06-22 23:02:51 +02:00
|
|
|
:+ print
|
2020-04-12 16:38:57 +02:00
|
|
|
|
2020-11-29 14:36:24 +01:00
|
|
|
Note that the colon is not required for the |+cmd| argument: >
|
|
|
|
edit +6 fname
|
|
|
|
|
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-12-23 13:56:35 +01:00
|
|
|
Since a continuation line cannot be easily recognized the parsing of commands
|
2020-12-24 15:14:01 +01:00
|
|
|
has been made stricter. E.g., because of the error in the first line, the
|
2020-12-23 13:56:35 +01:00
|
|
|
second line is seen as a separate command: >
|
|
|
|
popup_create(some invalid expression, {
|
|
|
|
exit_cb: Func})
|
|
|
|
Now "exit_cb: Func})" is actually a valid command: save any changes to the
|
|
|
|
file "_cb: Func})" and exit. To avoid this kind of mistake in Vim9 script
|
|
|
|
there must be white space between most command names and the argument.
|
|
|
|
|
|
|
|
|
2020-07-10 22:00:53 +02:00
|
|
|
Notes:
|
|
|
|
- "enddef" cannot be used at the start of a continuation line, it ends the
|
|
|
|
current function.
|
|
|
|
- No line break is allowed in the LHS of an assignment. Specifically when
|
|
|
|
unpacking a list |:let-unpack|. This is OK: >
|
2020-09-07 22:18:52 +02:00
|
|
|
[var1, var2] =
|
2020-07-10 22:00:53 +02:00
|
|
|
Func()
|
|
|
|
< This does not work: >
|
2020-09-07 22:18:52 +02:00
|
|
|
[var1,
|
2020-07-10 22:00:53 +02:00
|
|
|
var2] =
|
|
|
|
Func()
|
|
|
|
- No line break is allowed in between arguments of an `:echo`, `:execute` and
|
|
|
|
similar commands. This is OK: >
|
2020-09-07 22:18:52 +02:00
|
|
|
echo [1,
|
2020-07-10 22:00:53 +02:00
|
|
|
2] [3,
|
|
|
|
4]
|
|
|
|
< This does not work: >
|
2020-09-07 22:18:52 +02:00
|
|
|
echo [1, 2]
|
2020-07-10 22:00:53 +02:00
|
|
|
[3, 4]
|
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-11-19 18:53:18 +01:00
|
|
|
Dictionary literals ~
|
|
|
|
|
|
|
|
Traditionally Vim has supported dictionary literals with a {} syntax: >
|
|
|
|
let dict = {'key': value}
|
|
|
|
|
2020-12-04 19:12:14 +01:00
|
|
|
Later it became clear that using a simple text key is very common, thus
|
|
|
|
literal dictionaries were introduced in a backwards compatible way: >
|
2020-11-19 18:53:18 +01:00
|
|
|
let dict = #{key: value}
|
|
|
|
|
2020-12-04 19:12:14 +01:00
|
|
|
However, this #{} syntax is unlike any existing language. As it turns out
|
|
|
|
that using a literal key is much more common than using an expression, and
|
2020-11-19 18:53:18 +01:00
|
|
|
considering that JavaScript uses this syntax, using the {} form for dictionary
|
2020-12-04 19:12:14 +01:00
|
|
|
literals is considered a much more useful syntax. In Vim9 script the {} form
|
2020-11-19 18:53:18 +01:00
|
|
|
uses literal keys: >
|
|
|
|
let dict = {key: value}
|
|
|
|
|
2020-12-04 19:12:14 +01:00
|
|
|
This works for alphanumeric characters, underscore and dash. If you want to
|
|
|
|
use another character, use a single or double quoted string: >
|
|
|
|
let dict = {'key with space': value}
|
|
|
|
let dict = {"key\twith\ttabs": value}
|
|
|
|
let dict = {'': value} # empty key
|
|
|
|
|
|
|
|
In case the key needs to be an expression, square brackets can be used, just
|
|
|
|
like in JavaScript: >
|
2020-11-19 18:53:18 +01:00
|
|
|
let dict = {["key" .. nr]: value}
|
|
|
|
|
|
|
|
|
2020-08-01 17:00:03 +02:00
|
|
|
No :xit, :t, :append, :change or :insert ~
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-01 17:00:03 +02:00
|
|
|
These commands are too easily confused with local variable names.
|
|
|
|
Instead of `:x` or `:xit` you can use `:exit`.
|
|
|
|
Instead of `:t` you can use `:copy`.
|
2020-02-04 22:53:05 +01:00
|
|
|
|
|
|
|
|
|
|
|
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: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var name=234 # Error!
|
|
|
|
var name= 234 # Error!
|
|
|
|
var name =234 # Error!
|
2020-01-26 15:56:19 +01:00
|
|
|
There must be white space before and after the "=": >
|
2020-09-26 15:09:30 +02:00
|
|
|
var name = 234 # OK
|
2020-07-10 22:00:53 +02:00
|
|
|
White space must also be put before the # that starts a comment after a
|
|
|
|
command: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var name = 234# Error!
|
|
|
|
var name = 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 "(": >
|
2020-09-07 22:18:52 +02:00
|
|
|
call Func (arg) # Error!
|
|
|
|
call Func
|
|
|
|
\ (arg) # Error!
|
|
|
|
call Func(arg) # OK
|
|
|
|
call Func(
|
|
|
|
\ arg) # OK
|
|
|
|
call Func(
|
|
|
|
\ arg # OK
|
2020-02-21 18:42:43 +01:00
|
|
|
\ )
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
Conditions and expressions ~
|
|
|
|
|
2020-10-04 16:06:05 +02:00
|
|
|
Conditions and expressions are mostly working like they do in other languages.
|
|
|
|
Some values are different from legacy Vim script:
|
|
|
|
value legacy Vim script Vim9 script ~
|
|
|
|
0 falsy falsy
|
|
|
|
1 truthy truthy
|
|
|
|
99 truthy Error!
|
|
|
|
"0" falsy Error!
|
|
|
|
"99" truthy Error!
|
|
|
|
"text" falsy Error!
|
|
|
|
|
|
|
|
For the "??" operator and when using "!" then there is no error, every value
|
|
|
|
is either falsy or truthy. This is mostly like JavaScript, except that an
|
|
|
|
empty list and dict is falsy:
|
|
|
|
|
|
|
|
type truthy when ~
|
2020-09-14 21:39:44 +02:00
|
|
|
bool v:true or 1
|
2020-01-26 15:56:19 +01:00
|
|
|
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)
|
|
|
|
|
2020-10-03 22:52:39 +02:00
|
|
|
The boolean operators "||" and "&&" expect the values to be boolean, zero or
|
|
|
|
one: >
|
|
|
|
1 || false == true
|
|
|
|
0 || 1 == true
|
|
|
|
0 || false == false
|
|
|
|
1 && true == true
|
|
|
|
0 && 1 == false
|
|
|
|
8 || 0 Error!
|
|
|
|
'yes' && 0 Error!
|
|
|
|
[] || 99 Error!
|
|
|
|
|
|
|
|
When using "!" for inverting, there is no error for using any type and the
|
2020-10-04 16:06:05 +02:00
|
|
|
result is a boolean. "!!" can be used to turn any value into boolean: >
|
2020-10-03 22:52:39 +02:00
|
|
|
!'yes' == false
|
2020-10-04 16:06:05 +02:00
|
|
|
!![] == false
|
|
|
|
!![1, 2, 3] == true
|
2020-10-03 22:52:39 +02:00
|
|
|
|
|
|
|
When using "`.."` for string concatenation arguments of simple types are
|
2020-10-04 16:06:05 +02:00
|
|
|
always converted to string: >
|
2020-01-26 15:56:19 +01:00
|
|
|
'hello ' .. 123 == 'hello 123'
|
2020-08-15 18:55:18 +02:00
|
|
|
'hello ' .. v:true == 'hello v:true'
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-12 21:34:49 +02:00
|
|
|
Simple types are string, float, special and bool. For other types |string()|
|
|
|
|
can be used.
|
2020-09-07 22:18:52 +02:00
|
|
|
*false* *true*
|
2020-01-26 15:56:19 +01:00
|
|
|
In Vim9 script one can use "true" for v:true and "false" for v:false.
|
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
Indexing a string with [idx] or [idx, idx] uses character indexes instead of
|
|
|
|
byte indexes. Example: >
|
|
|
|
echo 'bár'[1]
|
|
|
|
In legacy script this results in the character 0xc3 (an illegal byte), in Vim9
|
|
|
|
script this results in the string 'á'.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-06-30 20:38:27 +02:00
|
|
|
What to watch out for ~
|
|
|
|
*vim9-gotchas*
|
|
|
|
Vim9 was designed to be closer to often used programming languages, but at the
|
|
|
|
same time tries to support the legacy Vim commands. Some compromises had to
|
|
|
|
be made. Here is a summary of what might be unexpected.
|
|
|
|
|
|
|
|
Ex command ranges need to be prefixed with a colon. >
|
2020-09-07 22:18:52 +02:00
|
|
|
-> # legacy Vim: shifts the previous line to the right
|
|
|
|
->func() # Vim9: method call in continuation line
|
|
|
|
:-> # Vim9: shifts the previous line to the right
|
2020-06-30 20:38:27 +02:00
|
|
|
|
2020-09-07 22:18:52 +02:00
|
|
|
%s/a/b # legacy Vim: substitute on all lines
|
2020-06-30 20:38:27 +02:00
|
|
|
x = alongname
|
2020-09-07 22:18:52 +02:00
|
|
|
% another # Vim9: line continuation without a backslash
|
|
|
|
:%s/a/b # Vim9: substitute on all lines
|
|
|
|
'text'->func() # Vim9: method call
|
|
|
|
:'t # legacy Vim: jump to mark m
|
2020-06-30 20:38:27 +02:00
|
|
|
|
2020-08-07 19:54:59 +02:00
|
|
|
Some Ex commands can be confused with assignments in Vim9 script: >
|
|
|
|
g:name = value # assignment
|
|
|
|
g:pattern:cmd # invalid command - ERROR
|
|
|
|
:g:pattern:cmd # :global command
|
|
|
|
|
2020-06-30 20:38:27 +02:00
|
|
|
Functions defined with `:def` compile the whole function. Legacy functions
|
|
|
|
can bail out, and the following lines are not parsed: >
|
|
|
|
func Maybe()
|
|
|
|
if !has('feature')
|
|
|
|
return
|
|
|
|
endif
|
|
|
|
use-feature
|
|
|
|
endfunc
|
|
|
|
Vim9 functions are compiled as a whole: >
|
|
|
|
def Maybe()
|
|
|
|
if !has('feature')
|
|
|
|
return
|
|
|
|
endif
|
2020-09-07 22:18:52 +02:00
|
|
|
use-feature # May give compilation error
|
2020-06-30 20:38:27 +02:00
|
|
|
enddef
|
|
|
|
For a workaround, split it in two functions: >
|
|
|
|
func Maybe()
|
|
|
|
if has('feature')
|
|
|
|
call MaybyInner()
|
|
|
|
endif
|
|
|
|
endfunc
|
|
|
|
if has('feature')
|
|
|
|
def MaybeInner()
|
|
|
|
use-feature
|
|
|
|
enddef
|
|
|
|
endif
|
2020-09-07 22:18:52 +02:00
|
|
|
Or put the unsupported code inside an `if` with a constant expression that
|
2020-08-30 17:20:20 +02:00
|
|
|
evaluates to false: >
|
|
|
|
def Maybe()
|
|
|
|
if has('feature')
|
|
|
|
use-feature
|
|
|
|
endif
|
|
|
|
enddef
|
|
|
|
Note that for unrecognized commands there is no check for "|" and a following
|
|
|
|
command. This will give an error for missing `endif`: >
|
|
|
|
def Maybe()
|
|
|
|
if has('feature') | use-feature | endif
|
|
|
|
enddef
|
2020-06-30 20:38:27 +02:00
|
|
|
|
2020-12-23 13:56:35 +01:00
|
|
|
Other differences ~
|
|
|
|
|
|
|
|
Patterns are used like 'magic' is set, unless explicitly overruled.
|
|
|
|
The 'edcompatible' option value is not used.
|
|
|
|
The 'gdefault' option value is not used.
|
|
|
|
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
3. New style functions *fast-functions*
|
|
|
|
|
|
|
|
THIS IS STILL UNDER DEVELOPMENT - ANYTHING CAN BREAK - ANYTHING CAN CHANGE
|
|
|
|
|
|
|
|
*:def*
|
2020-08-15 18:55:18 +02:00
|
|
|
:def[!] {name}([arguments])[: {return-type}]
|
2020-01-26 15:56:19 +01:00
|
|
|
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-06-14 17:29:55 +02:00
|
|
|
The function will be compiled into instructions when
|
2020-07-26 17:00:44 +02:00
|
|
|
called, or when `:disassemble` or `:defcompile` is
|
|
|
|
used. Syntax and type errors will be produced at that
|
|
|
|
time.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-07-26 17:00:44 +02:00
|
|
|
It is possible to nest `:def` inside another `:def` or
|
|
|
|
`:function` up to about 50 levels deep.
|
2020-02-04 22:53:05 +01:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
[!] is used as with `:function`. Note that
|
|
|
|
script-local functions cannot be deleted or redefined
|
|
|
|
later in Vim9 script. They can only be removed by
|
|
|
|
reloading the same script.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
*:enddef*
|
2020-07-26 17:00:44 +02:00
|
|
|
:enddef End of a function defined with `:def`. It should be on
|
|
|
|
a line by its own.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
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
|
2020-06-14 17:29:55 +02:00
|
|
|
before the function is compiled. If the script the function is defined in is
|
|
|
|
legacy script, then script-local variables must be accessed with the "s:"
|
2020-08-30 17:20:20 +02:00
|
|
|
prefix and they do not need to exist (they can be deleted any time).
|
2020-02-21 18:42:43 +01:00
|
|
|
|
2020-05-26 21:20:45 +02:00
|
|
|
*:defc* *:defcompile*
|
|
|
|
:defc[ompile] Compile functions defined in the current script that
|
|
|
|
were not compiled yet.
|
|
|
|
This will report errors found during the compilation.
|
2020-02-21 18:42:43 +01:00
|
|
|
|
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-07-10 22:00:53 +02:00
|
|
|
Limitations ~
|
|
|
|
|
|
|
|
Local variables will not be visible to string evaluation. For example: >
|
2020-12-26 15:39:31 +01:00
|
|
|
def MapList(): list<string>
|
2020-09-26 15:09:30 +02:00
|
|
|
var list = ['aa', 'bb', 'cc', 'dd']
|
2020-07-10 22:00:53 +02:00
|
|
|
return range(1, 2)->map('list[v:val]')
|
|
|
|
enddef
|
|
|
|
|
|
|
|
The map argument is a string expression, which is evaluated without the
|
|
|
|
function scope. Instead, use a lambda: >
|
2020-12-26 15:39:31 +01:00
|
|
|
def MapList(): list<string>
|
2020-09-26 15:09:30 +02:00
|
|
|
var list = ['aa', 'bb', 'cc', 'dd']
|
2020-12-26 15:39:31 +01:00
|
|
|
return range(1, 2)->map(( _, v) => list[v])
|
2020-07-10 22:00:53 +02:00
|
|
|
enddef
|
|
|
|
|
2020-12-26 15:39:31 +01:00
|
|
|
The same is true for commands that are not compiled, such as `:global`.
|
|
|
|
For these the backtick expansion can be used. Example: >
|
|
|
|
def Replace()
|
|
|
|
var newText = 'blah'
|
|
|
|
g/pattern/s/^/`=newText`/
|
|
|
|
enddef
|
2020-07-10 22:00:53 +02: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:
|
2020-07-26 17:00:44 +02:00
|
|
|
{type}|{type} {not implemented yet}
|
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>
|
2020-08-09 17:22:04 +02:00
|
|
|
Custom types must start with a capital letter, to avoid name clashes with
|
|
|
|
builtin types added later, similarly to user functions.
|
2020-01-26 15:56:19 +01:00
|
|
|
{not implemented yet}
|
|
|
|
|
|
|
|
And classes and interfaces can be used as types: >
|
|
|
|
:class MyClass
|
2020-09-26 15:09:30 +02:00
|
|
|
:var mine: MyClass
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
:interface MyInterface
|
2020-09-26 15:09:30 +02:00
|
|
|
:var mine: MyInterface
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
:class MyTemplate<Targ>
|
2020-09-26 15:09:30 +02:00
|
|
|
:var mine: MyTemplate<number>
|
|
|
|
:var mine: MyTemplate<string>
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
:class MyInterface<Targ>
|
2020-09-26 15:09:30 +02:00
|
|
|
:var mine: MyInterface<number>
|
|
|
|
:var mine: MyInterface<string>
|
2020-01-26 15:56:19 +01:00
|
|
|
{not implemented yet}
|
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Variable types and type casting ~
|
|
|
|
*variable-types*
|
2020-08-09 19:02:50 +02:00
|
|
|
Variables declared in Vim9 script or in a `:def` function have a type, either
|
|
|
|
specified explicitly or inferred from the initialization.
|
|
|
|
|
|
|
|
Global, buffer, window and tab page variables do not have a specific type, the
|
|
|
|
value can be changed at any time, possibly changing the type. Therefore, in
|
|
|
|
compiled code the "any" type is assumed.
|
|
|
|
|
|
|
|
This can be a problem when the "any" type is undesired and the actual type is
|
|
|
|
expected to always be the same. For example, when declaring a list: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var l: list<number> = [1, g:two]
|
2020-12-23 13:56:35 +01:00
|
|
|
At compile time Vim doesn't know the type of "g:two" and the expression type
|
|
|
|
becomes list<any>. An instruction is generated to check the list type before
|
|
|
|
doing the assignment, which is a bit inefficient.
|
|
|
|
*type-casting*
|
|
|
|
To avoid this, use a type cast: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var l: list<number> = [1, <number>g:two]
|
2020-12-23 13:56:35 +01:00
|
|
|
The compiled code will then only check that "g:two" is a number and give an
|
|
|
|
error if it isn't. This is called type casting.
|
2020-08-09 19:02:50 +02:00
|
|
|
|
|
|
|
The syntax of a type cast is: "<" {type} ">". There cannot be white space
|
|
|
|
after the "<" or before the ">" (to avoid them being confused with
|
|
|
|
smaller-than and bigger-than operators).
|
|
|
|
|
|
|
|
The semantics is that, if needed, a runtime type check is performed. The
|
|
|
|
value is not actually changed. If you need to change the type, e.g. to change
|
|
|
|
it to a string, use the |string()| function. Or use |str2nr()| to convert a
|
|
|
|
string to a number.
|
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Type inference ~
|
|
|
|
*type-inference*
|
2020-01-26 15:56:19 +01:00
|
|
|
In general: Whenever the type is clear it can be omitted. For example, when
|
|
|
|
declaring a variable and giving it a value: >
|
2020-09-26 15:09:30 +02:00
|
|
|
var name = 0 # infers number type
|
|
|
|
var name = 'hello' # infers string type
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-09 17:22:04 +02:00
|
|
|
The type of a list and dictionary comes from the common type of the values.
|
|
|
|
If the values all have the same type, that type is used for the list or
|
|
|
|
dictionary. If there is a mix of types, the "any" type is used. >
|
|
|
|
[1, 2, 3] list<number>
|
|
|
|
['a', 'b', 'c'] list<string>
|
|
|
|
[1, 'x', 3] list<any>
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-30 17:20:20 +02:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Stricter type checking ~
|
|
|
|
*type-checking*
|
2020-08-30 17:20:20 +02:00
|
|
|
In legacy Vim script, where a number was expected, a string would be
|
|
|
|
automatically converted to a number. This was convenient for an actual number
|
|
|
|
such as "123", but leads to unexpected problems (but no error message) if the
|
|
|
|
string doesn't start with a number. Quite often this leads to hard-to-find
|
|
|
|
bugs.
|
|
|
|
|
|
|
|
In Vim9 script this has been made stricter. In most places it works just as
|
2020-09-07 22:18:52 +02:00
|
|
|
before, if the value used matches the expected type. There will sometimes be
|
|
|
|
an error, thus breaking backwards compatibility. For example:
|
2020-08-30 17:20:20 +02:00
|
|
|
- Using a number other than 0 or 1 where a boolean is expected. *E1023*
|
|
|
|
- Using a string value when setting a number options.
|
|
|
|
- Using a number where a string is expected. *E1024*
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
==============================================================================
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
5. Namespace, Import and Export
|
2020-01-26 15:56:19 +01:00
|
|
|
*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.
|
|
|
|
|
2020-08-30 17:20:20 +02:00
|
|
|
You can cheat by using the global namespace explicitly. We will assume here
|
|
|
|
that you don't do that.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Namespace ~
|
2020-12-28 20:53:21 +01:00
|
|
|
*vim9-namespace*
|
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
|
2020-09-26 15:09:30 +02:00
|
|
|
var myvar = 'yes'
|
2020-01-26 15:56:19 +01:00
|
|
|
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*
|
2020-07-26 17:00:44 +02:00
|
|
|
Exporting an item can be written as: >
|
2020-01-26 15:56:19 +01:00
|
|
|
export const EXPORTED_CONST = 1234
|
2020-09-26 15:09:30 +02:00
|
|
|
export var someValue = ...
|
|
|
|
export final someValue = ...
|
|
|
|
export const someValue = ...
|
2020-01-26 15:56:19 +01:00
|
|
|
export def MyFunc() ...
|
|
|
|
export class MyClass ...
|
|
|
|
|
|
|
|
As this suggests, only constants, variables, `:def` functions and classes can
|
2020-07-26 17:00:44 +02:00
|
|
|
be exported. {classes are not implemented yet}
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-06-14 17:29:55 +02:00
|
|
|
*E1042*
|
|
|
|
`:export` can only be used in Vim9 script, at the script level.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
|
|
|
Import ~
|
2020-06-21 22:12:03 +02:00
|
|
|
*:import* *:imp* *E1094*
|
2020-01-26 15:56:19 +01:00
|
|
|
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'
|
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
{not implemented yet: using "This as That"}
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
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.
|
|
|
|
|
2020-09-07 22:18:52 +02:00
|
|
|
`:import` can also be used in legacy Vim script. The imported items still
|
|
|
|
become script-local, even when the "s:" prefix is not given.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
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
|
2020-10-26 21:12:46 +01:00
|
|
|
will rarely be used.
|
2020-01-26 15:56:19 +01:00
|
|
|
- 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.
|
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
2. In the autoload script do the actual work. You can import items from
|
2020-01-26 15:56:19 +01:00
|
|
|
other files to split up functionality in appropriate pieces. >
|
|
|
|
vim9script
|
|
|
|
import FilterFunc from "../import/someother.vim"
|
|
|
|
def searchfor#Stuff(arg: string)
|
2020-09-26 15:09:30 +02:00
|
|
|
var filtered = FilterFunc(arg)
|
2020-01-26 15:56:19 +01:00
|
|
|
...
|
|
|
|
< 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
|
2020-09-26 15:09:30 +02:00
|
|
|
var localVar = 'local'
|
2020-09-07 22:18:52 +02:00
|
|
|
export def FilterFunc(arg: string): string
|
2020-01-26 15:56:19 +01:00
|
|
|
...
|
|
|
|
< This goes in .../import/someother.vim.
|
|
|
|
|
2020-08-12 21:34:49 +02:00
|
|
|
When compiling a `:def` function and a function in an autoload script is
|
|
|
|
encountered, the script is not loaded until the `:def` function is called.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Import in legacy Vim script ~
|
|
|
|
|
2020-06-14 17:29:55 +02:00
|
|
|
If an `import` statement is used in legacy Vim script, the script-local "s:"
|
|
|
|
namespace will be used for the imported item, even when "s:" is not specified.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
6. Future work: classes *vim9-classes*
|
|
|
|
|
|
|
|
Above "class" was mentioned a few times, but it has not been implemented yet.
|
2020-09-26 15:09:30 +02:00
|
|
|
Most of Vim9 script can be created without this functionality, and since
|
2020-09-19 18:50:13 +02:00
|
|
|
implementing classes is going to be a lot of work, it is left for the future.
|
|
|
|
For now we'll just make sure classes can be added later.
|
|
|
|
|
|
|
|
Thoughts:
|
|
|
|
- `class` / `endclass`, everything in one file
|
|
|
|
- Class names are always CamelCase
|
|
|
|
- Single constructor
|
|
|
|
- Single inheritance with `class ThisClass extends BaseClass`
|
|
|
|
- `abstract class`
|
|
|
|
- `interface` (Abstract class without any implementation)
|
|
|
|
- `class SomeClass implements SomeInterface`
|
|
|
|
- Generics for class: `class <Tkey, Tentry>`
|
|
|
|
- Generics for function: `def <Tkey> GetLast(key: Tkey)`
|
|
|
|
|
|
|
|
Again, much of this is from TypeScript.
|
|
|
|
|
|
|
|
Some things that look like good additions:
|
|
|
|
- Use a class as an interface (like Dart)
|
|
|
|
- Extend a class with methods, using an import (like Dart)
|
|
|
|
|
|
|
|
An important class that will be provided is "Promise". Since Vim is single
|
|
|
|
threaded, connecting asynchronous operations is a natural way of allowing
|
|
|
|
plugins to do their work without blocking the user. It's a uniform way to
|
|
|
|
invoke callbacks and handle timeouts and errors.
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
==============================================================================
|
|
|
|
|
|
|
|
9. Rationale *vim9-rationale*
|
|
|
|
|
|
|
|
The :def command ~
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Plugin writers have asked for much faster Vim script. Investigations 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
|
2020-09-26 15:09:30 +02:00
|
|
|
considered the best way to separate the legacy style code from Vim9 style code.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
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
|
2020-09-26 15:09:30 +02:00
|
|
|
given at compile time, no error handling is needed at runtime, since adding
|
|
|
|
two numbers cannot fail.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
The syntax for types, using <type> for compound types, is similar to Java. It
|
|
|
|
is easy to understand and widely used. The type names are what were used in
|
|
|
|
Vim before, with some additions such as "void" and "bool".
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
Removing clutter and weirdness ~
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
Once decided that `:def` functions have different syntax than legacy functions,
|
|
|
|
we are free to add improvements to make the code more familiar for users who
|
|
|
|
know popular programming languages. In other words: remove weird things that
|
2020-09-26 15:09:30 +02:00
|
|
|
only Vim does.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
We can also remove clutter, mainly things that were done to make Vim script
|
2020-09-26 15:09:30 +02:00
|
|
|
backwards compatible with the good old Vi commands.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
Examples:
|
|
|
|
- Drop `:call` for calling a function and `:eval` for manipulating data.
|
|
|
|
- Drop using a leading backslash for line continuation, automatically figure
|
|
|
|
out where an expression ends.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
However, this does require that some things need to change:
|
|
|
|
- Comments start with # instead of ", to avoid confusing them with strings.
|
2020-09-26 15:09:30 +02:00
|
|
|
This is good anyway, it is known from several popular languages.
|
2020-09-19 18:50:13 +02:00
|
|
|
- Ex command ranges need to be prefixed with a colon, to avoid confusion with
|
|
|
|
expressions (single quote can be a string or a mark, "/" can be divide or a
|
|
|
|
search command, etc.).
|
|
|
|
|
|
|
|
Goal is to limit the differences. A good criteria is that when the old syntax
|
2020-09-26 15:09:30 +02:00
|
|
|
is accidentally used you are very likely to get an error message.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Syntax and semantics from popular languages ~
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Script writers have complained that the Vim script syntax is unexpectedly
|
|
|
|
different from what they are used to. To reduce this complaint popular
|
2020-06-14 17:29:55 +02:00
|
|
|
languages are used as an example. At the same time, we do not want to abandon
|
|
|
|
the well-known parts of legacy Vim script.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
For many things TypeScript is followed. It's a recent language that is
|
|
|
|
gaining popularity and has similarities with Vim script. It also has a
|
2020-09-19 18:50:13 +02:00
|
|
|
mix of static typing (a variable always has a known value type) and dynamic
|
2020-09-26 15:09:30 +02:00
|
|
|
typing (a variable can have different types, this changes at runtime). Since
|
2020-09-19 18:50:13 +02:00
|
|
|
legacy Vim script is dynamically typed and a lot of existing functionality
|
|
|
|
(esp. builtin functions) depends on that, while static typing allows for much
|
|
|
|
faster execution, we need to have this mix in Vim9 script.
|
|
|
|
|
2020-09-07 22:18:52 +02:00
|
|
|
There is no intention to completely match TypeScript syntax and semantics. We
|
|
|
|
just want to take those parts that we can use for Vim and we expect Vim users
|
2020-09-19 18:50:13 +02:00
|
|
|
will be happy with. TypeScript is a complex language with its own history,
|
|
|
|
advantages and disadvantages. To get an idea of the disadvantages read the
|
|
|
|
book: "JavaScript: The Good Parts". Or find the article "TypeScript: the good
|
2020-09-14 21:39:44 +02:00
|
|
|
parts" and read the "Things to avoid" section.
|
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
People familiar with other languages (Java, Python, etc.) will also find
|
|
|
|
things in TypeScript that they do not like or do not understand. We'll try to
|
|
|
|
avoid those things.
|
2020-09-14 21:39:44 +02:00
|
|
|
|
|
|
|
Specific items from TypeScript we avoid:
|
|
|
|
- Overloading "+", using it both for addition and string concatenation. This
|
|
|
|
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. And it allows for conversion to string for more values.
|
|
|
|
- TypeScript can use an expression like "99 || 'yes'" in a condition, but
|
|
|
|
cannot assign the value to a boolean. That is inconsistent and can be
|
|
|
|
annoying. Vim recognizes an expression with && or || and allows using the
|
2020-09-26 15:09:30 +02:00
|
|
|
result as a bool. TODO: to be reconsidered
|
2020-09-14 21:39:44 +02:00
|
|
|
- TypeScript considers an empty string as Falsy, but an empty list or dict as
|
|
|
|
Truthy. That is inconsistent. In Vim an empty list and dict are also
|
|
|
|
Falsy.
|
|
|
|
- TypeScript has various "Readonly" types, which have limited usefulness,
|
|
|
|
since a type cast can remove the immutable nature. Vim locks the value,
|
|
|
|
which is more flexible, but is only checked at runtime.
|
2020-09-07 22:18:52 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Declarations ~
|
|
|
|
|
|
|
|
Legacy Vim script uses `:let` for every assignment, while in Vim9 declarations
|
|
|
|
are used. That is different, thus it's good to use a different command:
|
|
|
|
`:var`. This is used in many languages. The semantics might be slightly
|
|
|
|
different, but it's easily recognized as a declaration.
|
|
|
|
|
2020-11-29 14:36:24 +01:00
|
|
|
Using `:const` for constants is common, but the semantics varies. Some
|
2020-09-26 15:09:30 +02:00
|
|
|
languages only make the variable immutable, others also make the value
|
|
|
|
immutable. Since "final" is well known from Java for only making the variable
|
|
|
|
immutable we decided to use that. And then `:const` can be used for making
|
|
|
|
both immutable. This was also used in legacy Vim script and the meaning is
|
|
|
|
almost the same.
|
|
|
|
|
|
|
|
What we end up with is very similar to Dart: >
|
|
|
|
:var name # mutable variable and value
|
|
|
|
:final name # immutable variable, mutable value
|
|
|
|
:const name # immutable variable and value
|
|
|
|
|
|
|
|
Since legacy and Vim9 script will be mixed and global variables will be
|
|
|
|
shared, optional type checking is desirable. Also, type inference will avoid
|
|
|
|
the need for specifying the type in many cases. The TypeScript syntax fits
|
|
|
|
best for adding types to declarations: >
|
|
|
|
var name: string # string type is specified
|
|
|
|
...
|
|
|
|
name = 'John'
|
|
|
|
const greeting = 'hello' # string type is inferred
|
|
|
|
|
|
|
|
This is how we put types in a declaration: >
|
|
|
|
var mylist: list<string>
|
|
|
|
final mylist: list<string> = ['foo']
|
|
|
|
def Func(arg1: number, arg2: string): bool
|
|
|
|
|
|
|
|
Two alternatives were considered:
|
|
|
|
1. Put the type before the name, like Dart: >
|
|
|
|
var list<string> mylist
|
|
|
|
final list<string> mylist = ['foo']
|
|
|
|
def Func(number arg1, string arg2) bool
|
|
|
|
2. Put the type after the variable name, but do not use a colon, like Go: >
|
|
|
|
var mylist list<string>
|
|
|
|
final mylist list<string> = ['foo']
|
|
|
|
def Func(arg1 number, arg2 string) bool
|
|
|
|
|
|
|
|
The first is more familiar for anyone used to C or Java. The second one
|
2020-10-11 13:57:40 +02:00
|
|
|
doesn't really have an advantage over the first, so let's discard the second.
|
2020-09-26 15:09:30 +02:00
|
|
|
|
|
|
|
Since we use type inference the type can be left out when it can be inferred
|
|
|
|
from the value. This means that after `var` we don't know if a type or a name
|
|
|
|
follows. That makes parsing harder, not only for Vim but also for humans.
|
|
|
|
Also, it will not be allowed to use a variable name that could be a type name,
|
|
|
|
using `var string string` is too confusing.
|
|
|
|
|
|
|
|
The chosen syntax, using a colon to separate the name from the type, adds
|
|
|
|
punctuation, but it actually makes it easier to recognize the parts of a
|
|
|
|
declaration.
|
|
|
|
|
|
|
|
|
|
|
|
Expressions ~
|
|
|
|
|
2020-10-11 13:57:40 +02:00
|
|
|
Expression evaluation was already close to what other languages are doing.
|
|
|
|
Some details are unexpected and can be improved. For example a boolean
|
|
|
|
condition would accept a string, convert it to a number and check if the
|
|
|
|
number is non-zero. This is unexpected and often leads to mistakes, since
|
|
|
|
text not starting with a number would be converted to zero, which is
|
2020-10-26 21:12:46 +01:00
|
|
|
considered false. Thus using a string for a condition would often not give an
|
|
|
|
error and be considered false. That is confusing.
|
2020-10-11 13:57:40 +02:00
|
|
|
|
2020-11-29 14:36:24 +01:00
|
|
|
In Vim9 type checking is stricter to avoid mistakes. Where a condition is
|
2020-10-11 13:57:40 +02:00
|
|
|
used, e.g. with the `:if` command and the `||` operator, only boolean-like
|
|
|
|
values are accepted:
|
|
|
|
true: `true`, `v:true`, `1`, `0 < 9`
|
|
|
|
false: `false`, `v:false`, `0`, `0 > 9`
|
|
|
|
Note that the number zero is false and the number one is true. This is more
|
2020-10-26 21:12:46 +01:00
|
|
|
permissive than most other languages. It was done because many builtin
|
2020-10-11 13:57:40 +02:00
|
|
|
functions return these values.
|
|
|
|
|
|
|
|
If you have any type of value and want to use it as a boolean, use the `!!`
|
|
|
|
operator:
|
|
|
|
true: !`!'text'`, `!![99]`, `!!{'x': 1}`, `!!99`
|
|
|
|
false: `!!''`, `!![]`, `!!{}`
|
|
|
|
|
|
|
|
From a language like JavaScript we have this handy construct: >
|
|
|
|
GetName() || 'unknown'
|
|
|
|
However, this conflicts with only allowing a boolean for a condition.
|
|
|
|
Therefore the "??" operator was added: >
|
|
|
|
GetName() ?? 'unknown'
|
|
|
|
Here you can explicitly express your intention to use the value as-is and not
|
|
|
|
result in a boolean. This is called the |falsy-operator|.
|
2020-09-26 15:09:30 +02:00
|
|
|
|
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
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
|
2020-09-07 22:18:52 +02:00
|
|
|
available in other scripts. This defies the concept of a package that only
|
|
|
|
exports selected items and keeps the rest local.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-08-15 18:55:18 +02:00
|
|
|
In Vim9 script a mechanism very similar to the JavaScript import and export
|
2020-01-26 15:56:19 +01:00
|
|
|
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.
|
2020-09-07 22:18:52 +02:00
|
|
|
- When importing a script the symbols that are imported are explicitly listed,
|
|
|
|
avoiding name conflicts and failures if functionality is added later.
|
2020-01-26 15:56:19 +01:00
|
|
|
- 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.
|
|
|
|
|
2020-06-14 17:29:55 +02:00
|
|
|
When sourcing a Vim9 script from a legacy script, only the items defined
|
|
|
|
globally can be used, not the exported items. Alternatives considered:
|
|
|
|
- All the exported items become available as script-local items. This makes
|
2020-09-07 22:18:52 +02:00
|
|
|
it uncontrollable what items get defined and likely soon leads to trouble.
|
2020-06-14 17:29:55 +02:00
|
|
|
- Use the exported items and make them global. Disadvantage is that it's then
|
|
|
|
not possible to avoid name clashes in the global namespace.
|
|
|
|
- Completely disallow sourcing a Vim9 script, require using `:import`. That
|
|
|
|
makes it difficult to use scripts for testing, or sourcing them from the
|
|
|
|
command line to try them out.
|
2020-09-07 22:18:52 +02:00
|
|
|
Note that you can also use `:import` in legacy Vim script, see above.
|
2020-06-14 17:29:55 +02:00
|
|
|
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
Compiling functions early ~
|
|
|
|
|
|
|
|
Functions are compiled when called or when `:defcompile` is used. Why not
|
|
|
|
compile them early, so that syntax and type errors are reported early?
|
|
|
|
|
|
|
|
The functions can't be compiled right away when encountered, because there may
|
|
|
|
be forward references to functions defined later. Consider defining functions
|
|
|
|
A, B and C, where A calls B, B calls C, and C calls A again. It's impossible
|
|
|
|
to reorder the functions to avoid forward references.
|
|
|
|
|
|
|
|
An alternative would be to first scan through the file to locate items and
|
|
|
|
figure out their type, so that forward references are found, and only then
|
|
|
|
execute the script and compile the functions. This means the script has to be
|
|
|
|
parsed twice, which is slower, and some conditions at the script level, such
|
|
|
|
as checking if a feature is supported, are hard to use. An attempt was made
|
|
|
|
to see if it works, but it turned out to be impossible to make work nicely.
|
|
|
|
|
|
|
|
It would be possible to compile all the functions at the end of the script.
|
|
|
|
The drawback is that if a function never gets called, the overhead of
|
|
|
|
compiling it counts anyway. Since startup speed is very important, in most
|
|
|
|
cases it's better to do it later and accept that syntax and type errors are
|
|
|
|
only reported then. In case these errors should be found early, e.g. when
|
|
|
|
testing, the `:defcompile` command will help out.
|
|
|
|
|
|
|
|
|
2020-09-26 15:09:30 +02:00
|
|
|
Why not use an embedded language? ~
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
Vim supports interfaces to Perl, Python, Lua, Tcl and a few others. But
|
2020-09-19 18:50:13 +02:00
|
|
|
these interfaces have never become widely used, for various reasons. When
|
|
|
|
Vim9 was designed a decision was made to make these interfaces lower priority
|
|
|
|
and concentrate on Vim script.
|
|
|
|
|
|
|
|
Still, plugin writers may find other languages more familiar, want to use
|
|
|
|
existing libraries or see a performance benefit. We encourage plugin authors
|
|
|
|
to write code in any language and run it as an external tool, using jobs and
|
|
|
|
channels. We can try to make this easier somehow.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
Using an external tool also has disadvantages. An alternative is to convert
|
2020-01-26 15:56:19 +01:00
|
|
|
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
|
2020-09-07 22:18:52 +02:00
|
|
|
support for classes in Vim is then a problem.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
2020-09-19 18:50:13 +02:00
|
|
|
|
|
|
|
Classes ~
|
|
|
|
|
|
|
|
Vim supports a kind-of object oriented programming by adding methods to a
|
|
|
|
dictionary. With some care this can be made to work, but it does not look
|
|
|
|
like real classes. On top of that, it's quite slow, because of the use of
|
|
|
|
dictionaries.
|
2020-01-26 15:56:19 +01:00
|
|
|
|
|
|
|
The support of classes in Vim9 script is a "minimal common functionality" of
|
2020-09-07 22:18:52 +02:00
|
|
|
class support in most languages. It works much like Java, which is the most
|
2020-01-26 15:56:19 +01:00
|
|
|
popular programming language.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
vim:tw=78:ts=8:noet:ft=help:norl:
|