runtime(doc): Update sections 5 to 8 in vim9.txt

closes: #18350

Co-authored-by: Aliaksei Budavei <0x000c70@gmail.com>
Signed-off-by: Peter Kenny <github.com@k1w1.cyou>
Signed-off-by: Christian Brabandt <cb@256bit.org>
This commit is contained in:
Peter Kenny
2025-10-06 18:31:45 +00:00
committed by Christian Brabandt
parent 723f34f3de
commit a51c53722c
2 changed files with 298 additions and 133 deletions

View File

@ -8049,6 +8049,7 @@ ge motion.txt /*ge*
gender-neutral helphelp.txt /*gender-neutral*
generic-function-call vim9.txt /*generic-function-call*
generic-function-declaration vim9.txt /*generic-function-declaration*
generic-function-example vim9.txt /*generic-function-example*
generic-functions vim9.txt /*generic-functions*
get() builtin.txt /*get()*
get()-blob builtin.txt /*get()-blob*
@ -11125,6 +11126,7 @@ type-casting vim9.txt /*type-casting*
type-checking vim9.txt /*type-checking*
type-inference vim9.txt /*type-inference*
type-mistakes tips.txt /*type-mistakes*
type-parameter-naming vim9.txt /*type-parameter-naming*
type-variable-naming vim9.txt /*type-variable-naming*
typealias vim9class.txt /*typealias*
typename() builtin.txt /*typename()*

View File

@ -1,4 +1,4 @@
*vim9.txt* For Vim version 9.1. Last change: 2025 Sep 30
*vim9.txt* For Vim version 9.1. Last change: 2025 Oct 06
VIM REFERENCE MANUAL by Bram Moolenaar
@ -18,9 +18,29 @@ features in Vim9 script.
5. Generic functions |generic-functions|
6. Namespace, Import and Export |vim9script|
7. Classes and interfaces |vim9-classes|
8. Rationale |vim9-rationale|
------------------------------------------------------------------------------
NOTE: In this vim9.txt help file, the Vim9 script code blocks beginning
with `vim9script` are Vim9 script syntax highlighted. Also, they are
sourceable, meaning you can run them to see what they output. To
source them, use `:'<,'>source` (see |:source-range|), which is done
by visually selecting the line(s) with |V| and typing `:so`.
For example, try it on the following Vim9 script: >vim9
vim9script
echowindow "Welcome to Vim9 script!"
<
There are also code examples that should not be sourced - they
explain concepts that don't require a sourceable example. Such code
blocks appear in generic code syntax highlighting, like this: >
def ThisFunction() # script-local
def g:ThatFunction() # global
export def Function() # for import and import autoload
==============================================================================
1. What is Vim9 script? *Vim9-script*
@ -1903,38 +1923,79 @@ A generic function allows using the same function with different type
arguments, while retaining type checking for arguments and the return value.
This provides type safety and code reusability.
Declaration~
*generic-function-declaration*
*E1553* *E1554* *E1559*
The type parameters for a generic function are declared in angle brackets "<"
and ">" directly after the function name. Multiple type names are separated
by commas: >
*E1553* *E1554*
The type variables for a generic function are declared as its type parameters
within angle brackets ("<" and ">"), directly after the function name.
Multiple type parameters are separated by commas: >
def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}]
{function body}
enddef
<
These type parameters can then be used like any other type within the function
signature and body. Example: >
< *generic-function-example*
These type parameters may then be used, like any other type, within the
function signature and its body. The following example combines two lists
into a list of tuples: >vim9
def MyFunc<T, A, B>(param1: T): T
var f: A
var x = param1
return x
vim9script
def Zip<T, U>(first: list<T>, second: list<U>): list<tuple<T, U>>
const LEN: number = ([first->len(), second->len()])->min()
final result: list<tuple<T, U>> = []
for i in range(LEN)
result->add((first[i], second[i]))
endfor
return result
enddef
var n: list<number> = [61, 62, 63]
var s: list<string> = ['a', 'b', 'c']
echo $"Zip example #1: {Zip<number, string>(n, s)}"
echo $"Zip example #2: {Zip<string, number>(s, n)}"
<
*type-variable-naming* *E1552*
The convention is to use a single uppercase letter for a type variable (e.g.,
T, A, X), although longer names are allowed. The name must start with an
uppercase letter.
*type-parameter-naming*
As in the preceding example, the convention is to use a single capital letter
for a name (e.g., T, U, A, etc.). Although they may comprise more than one
letter, names must start with a capital letter. In this example, "Ok" is
valid whereas "n" is not: >vim9
vim9script
def MyFail<Ok, n>(): void
enddef
# E1552: Type variable name must start with an uppercase letter: n...
<
*E1558* *E1560*
A function must be declared and used either as generic or as a regular
function - but not both.
A function must be declared and used either as a generic function or as a
regular function, but not both. The following Vim9 scripts demonstrate these
errors: >vim9
vim9script
My1558<number>()
# Vim(eval):E1558: Unknown generic function: My1558
< >vim9
vim9script
def My1560(): void
enddef
My1560<string>()
# Vim(echo):E1560: Not a generic function: My1560
<
*E1561*
A type variable name must not conflict with other defined names, such as class
names, type aliases, enum names, function names or other type variable names.
Type parameter names must not clash with other identifiers: >vim9
vim9script
def My1561<D, E, D>(): D
enddef
# E1561: Duplicate type variable name: D
vim9script
enum E
Yes, No
endenum
def My1041<E>(): E
enddef
# E0141: Redefining script item "E"
<
Calling a generic function~
*generic-function-call*
@ -1943,49 +2004,116 @@ between the function name and the argument list: >
MyFunc<number, string, list<number>>()
<
*E1555* *E1556* *E1557*
The number of concrete types provided when calling a generic function must
match the number of type variables in the function. An empty type list is not
allowed. Any Vim9 type (|vim9-types|) can be used as a concrete type in a
generic function.
NOTE: There are several working examples in this section, which may
be sourced, including |generic-function-example|.
Spaces are not allowed between the function name and "<", or between ">" and
the opening "(".
*E1555* *E1556* *E1557* *E1559*
The number of passed type arguments to the function must match the number
of its declared type parameters. An empty type list is not allowed.
Examples: >vim9
vim9script
def My1555<>(): void
enddef
# E1555: Empty type list specified for generic function ...
< >vim9
vim9script
def My1556<T>(): void
enddef
My1556<bool, bool>()
# E1556: Too many types specified for generic function ...
< >vim9
vim9script
def My1557<T, U>(): void
enddef
My1557<bool>()
# E1557: Not enough types specified for generic function ...
< >vim9
vim9script
def My1559<T>(): T
enddef
My1559()
# Vim(eval):E1559: Type arguments missing for generic function ...
<
Any Vim9 type (|vim9-types|) can be used as a concrete type in a generic
function.
Spaces are not allowed:
- Between the function name and "<" (|E1068|)
- Between ">" and the opening "(" (|E1068|), or
- Within the "<" and ">", except where required after the comma separating
the types (|E1202|).
A generic function can be exported and imported like a regular function.
See |:export| and |:import|.
A generic function can be defined inside another regular or generic function.
Example: >vim9
vim9script
def Outer(): void
# Returns either the first item of a list or a default value
def FirstOrDefault<T, U>(lst: list<T>, default: U): any
return lst->len() > 0 ? lst[0] : default
enddef
echo FirstOrDefault<string, bool>(['B', 'C'], false) # echos B
echo FirstOrDefault<number, number>([], 42) # echos 42
enddef
Outer()
<
Referencing type variables in generic types~
Using a type variable as a type argument ~
Instead of concrete types, type variables can be used with generic types.
This is useful for complex data structures like lists of dictionaries or
dictionaries of lists. Example: >
A type variable may also be passed as a type argument. For example: >vim9
vim9script
# T is declared as a type parameter
# It is used for the 'value' parameter and the return type
def Id<T>(value: T): T
return value
enddef
# U is declared as a type parameter
# It is used for the 'value' parameter and the return type
def CallId<U>(value: U): U
# U is a type variable passed/used as a type argument
return Id<U>(value)
enddef
echo CallId<string>('I am') .. ' ' .. CallId<number>(42)
This is useful for complex data structures like dictionaries of lists or,
as in the following example, lists of dictionaries: >vim9
vim9script
def Flatten<T>(x: list<list<T>>): list<T>
var result: list<T> = []
final result: list<T> = []
for inner in x
result += inner
result->extend(inner)
endfor
return result
enddef
echo Flatten<number>([[1, 2], [3]])
const ENGLISH: list<dict<string>> = [{1: 'one'}, {2: 'two'}]
const MANDARIN: list<dict<string>> = [{1: '壹'}, {2: '贰'}]
const ARABIC_N: list<dict<number>> = [{1: 1}, {2: 2}]
echo Flatten<dict<string>>([ENGLISH, MANDARIN])
echo Flatten<dict<any>>([ENGLISH, ARABIC_N])
<
In "Flatten<T>", "T" is a declared type parameter. Everywhere else in
the function, "T" is a type variable referencing that type parameter.
Generic class method~
A Vim9 class method can be a generic function: >
A Vim9 class method can be a generic function: >vim9
class A
def Foo<X, Y>()
vim9script
class Config
var settings: dict<any>
def Get<T>(key: string): T
return this.settings[key]
enddef
endclass
var a = A.new()
a.Foo<number, string>()
var c: Config = Config.new({timeout: 30, debug: true})
echo c.Get<number>('timeout')
echo c.Get<bool>('debug')
<
*E1432* *E1433* *E1434*
A generic class method in a base class can be overridden by a generic method
@ -1993,23 +2121,28 @@ in a child class. The number of type variables must match between both
methods. A concrete class method cannot be overridden by a generic method,
and vice versa.
Generic function reference~
A function reference (|Funcref|) can be a generic function. This allows for
creating factories of functions that operate on specific types: >
creating factories of functions that operate on specific types: >vim9
vim9script
def MakeEcho<T>(): func(T): T
return (x: T): T => x
# Match a specified character in a string or the decimal value of the
# character in a list. Note: '*' is decimal 42 (U+002A)
var c: string = "*"
var char_dec: tuple<string, string> = (c, c->char2nr()->string())
def Matcher<T>(pattern: string): func(T): bool
return (value: T): bool => match(value, pattern) >= 0
enddef
var EchoNumber = MakeEcho<number>()
echo EchoNumber(123)
var EchoString = MakeEcho<string>()
echo EchoString('abc')
var StringMatch = Matcher<string>(char_dec[0])
echo "*+"->StringMatch() # true (has *)
echo ",-"->StringMatch() # false
var ListMatch = Matcher<list<number>>(char_dec[1])
echo [42, 43]->ListMatch() # true (has 42)
echo [44, 45]->ListMatch() # false
<
Compiling and Disassembling Generic functions~
The |:defcompile| command can be used to compile a generic function with a
@ -2023,6 +2156,7 @@ a generic function: >
disassemble MyFunc<string, dict<string>>
disassemble MyFunc<number, list<blob>>
<
Limitations and Future Work~
Currently, Vim does not support:
@ -2085,22 +2219,35 @@ In the |vimrc| file sourced on startup this does not happen.
*vim9-mix*
There is one way to use both legacy and Vim9 syntax in one script file: >vim9
" legacy Vim script comments may go here
" _legacy Vim script_ comments here
if !has('vim9script')
" legacy Vim script commands go here
" _legacy Vim script_ comments/commands here
finish
endif
vim9script
# Vim9 script commands go here
# _Vim9 script_ commands/commands from here onwards
echowindow $"has('vim9script') == {has('vim9script')}"
<
This allows for writing a script that takes advantage of the Vim9 script
syntax if possible, but will also work on a Vim version without it.
syntax if possible, and prevents the vim9script command from throwing an
error if used in a version of Vim without 'vim9script'.
Note that Vim9 syntax changed before Vim 9 so that scripts using the current
syntax (such as "import from" instead of "import") might throw errors.
To prevent these, a safer check could be for |v:version| >= 900 instead.
To prevent these, a safer check may be |v:version| >= 900 instead (because
"has('vim9script')" will return `v:true` back to Vim 8.2 with patch 3965).
Sometimes it is prudent to cut off even later. Vim9 script's feature set
continues to grow so, for example, if tuples are used (introduced in Vim 9.1
patch 1232), a better condition is: >vim9
This can only work in two ways:
if !has('patch-9.1.1232')
echowindow $"Fail: Vim does not have patch 9.1.1232"
finish
endif
vim9script
echowindow $"Pass: version {v:versionlong}. Continuing ..."
<
Whichever vim-mix condition is used, it only works in one of two ways:
1. The "if" statement evaluates to false, the commands up to `endif` are
skipped and `vim9script` is then the first command actually executed.
2. The "if" statement evaluates to true, the commands up to `endif` are
@ -2245,43 +2392,47 @@ Importing an autoload script ~
For optimal startup speed, loading scripts should be postponed until they are
actually needed. Using the autoload mechanism is recommended:
*E1264*
1. In the plugin define user commands, functions and/or mappings that refer to
items imported from an autoload script. >
1. In the plugin, define user commands, functions and/or mappings
referring to items imported from an autoload script. >
import autoload 'for/search.vim'
command -nargs=1 SearchForStuff search.Stuff(<f-args>)
< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely chosen.
The "SearchForStuff" command is now available to the user.
< This goes in .../plugin/anyname.vim. "anyname.vim" can be freely
chosen. The "SearchForStuff" command is now available to the user.
The "autoload" argument to `:import` means that the script is not loaded
until one of the items is actually used. The script will be found under
the "autoload" directory in 'runtimepath' instead of the "import"
directory. Alternatively a relative or absolute name can be used, see
below.
The "autoload" argument to `:import` means that the script is not
loaded until one of the items is actually used. The script will be
found under the "autoload" directory in 'runtimepath' instead of the
"import" directory. Alternatively, either a relative or absolute
name can be used - see below.
2. In the autoload script put the bulk of the code. >
vim9script
export def Stuff(arg: string)
export def Stuff(arg: string): void
...
< This goes in .../autoload/for/search.vim.
Putting the "search.vim" script under the "/autoload/for/" directory has
the effect that "for#search#" will be prefixed to every exported item. The
prefix is obtained from the file name, as you would to manually in a
legacy autoload script. Thus the exported function can be found with
"for#search#Stuff", but you would normally use `import autoload` and not
use the prefix (which has the side effect of loading the autoload script
when compiling a function that encounters this name).
Putting the "search.vim" script under the "/autoload/for/" directory
has the effect that "for#search#" will be prefixed to every exported
item. The prefix is obtained from the file name, just as you would
add it manually in a legacy autoload script. Thus the exported
function can be found with "for#search#Stuff", but you would normally
use `import autoload` and not use the prefix (which has the side effect
of loading the autoload script when compiling a function that
encounters this name).
You can split up the functionality and import other scripts from the
autoload script as you like. This way you can share code between plugins.
autoload script as you like. This way you can share code between
plugins.
Searching for the autoload script in all entries in 'runtimepath' can be a bit
slow. If the plugin knows where the script is located, quite often a relative
path can be used. This avoids the search and should be quite a bit faster.
Another advantage is that the script name does not need to be unique. An
absolute path is also possible. Examples: >
Another advantage is that the script name does not need to be unique. Also,
an absolute path is possible. Examples: >
import autoload '../lib/implement.vim'
import autoload MyScriptsDir .. '/lib/implement.vim'
@ -2318,14 +2469,14 @@ Or: >
7. Classes and interfaces *vim9-classes*
In legacy script a Dictionary could be used as a kind-of object, by adding
In legacy Vim script, a Dictionary could be used as a kind-of object by adding
members that are functions. However, this is quite inefficient and requires
the writer to do the work of making sure all the objects have the right
members. See |Dictionary-function|.
In |Vim9| script you can have classes, objects and interfaces like in most
popular object-oriented programming languages. Since this is a lot of
functionality it is located in a separate help file: |vim9class.txt|.
In |Vim9| script you can have classes, objects, interfaces, and enums like
in most popular object-oriented programming languages. Since this is a lot
of functionality, it is located in a separate help file: |vim9class.txt|.
==============================================================================
@ -2362,11 +2513,19 @@ 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, since adding two numbers
cannot fail.
almost never fails.
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".
NOTE: As a tangential point, the exception is integer overflow, where the
result exceeds the maximum integer value. For example, adding to a 64-bit
signed integer where the result is greater than 2^63: >vim9
vim9script
echo 9223372036854775807 + 1 # -9223372036854775808
echo 2->pow(63)->float2nr() + 1 # -9223372036854775808
<
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".
Removing clutter and weirdness ~
@ -2481,7 +2640,7 @@ Two alternatives were considered:
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: >
< 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
@ -2521,10 +2680,14 @@ functions return these values, and changing that causes more problems than it
solves. After using this for a while it turned out to work well.
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: `!!''` `!![]` `!!{}`
operator (see |expr-!|): >vim9
vim9script
# The following are all true:
echo [!!'text', !![1], !!{'x': 1}, !!1, !!1.1]
# And these are all false:
echo [!!'', !![], !!{}, !!0, !!0.0]
<
From a language like JavaScript we have this handy construct: >
GetName() || 'unknown'
However, this conflicts with only allowing a boolean for a condition.
@ -2556,9 +2719,9 @@ that works like one would expect:
not needed.
- The Vim-specific use of "s:" to make things script-local can be dropped.
When sourcing a Vim9 script (from a Vim9 or legacy script), only the items
defined globally can be used, not the exported items. Alternatives
considered:
When sourcing a Vim9 script (from either a Vim9 script or legacy Vim 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
it uncontrollable what items get defined and likely soon leads to trouble.
- Use the exported items and make them global. Disadvantage is that it's then
@ -2609,8 +2772,8 @@ and channels. We can try to make this easier somehow.
Using an external tool also 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
support for classes in Vim is then a problem.
tool need to be supported. Since Vim9 script now includes support for
classes, objects, interfaces, and enums, that is increasingly feasible.