From 0d4f3eedf14f7c5fced8c3ca98b3b8b2fca54a1c Mon Sep 17 00:00:00 2001 From: kennypete <64727695+kennypete@users.noreply.github.com> Date: Sun, 5 Jul 2026 11:26:55 +1200 Subject: [PATCH] vim9.txt revisions, standardizing SS1,3-7 Signed-off-by: kennypete <64727695+kennypete@users.noreply.github.com> --- runtime/doc/vim9.txt | 2221 ++++++++++++++++++++++++------------------ 1 file changed, 1288 insertions(+), 933 deletions(-) diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 84502f19cf47ef..c238c4bf110815 100644 --- a/runtime/doc/vim9.txt +++ b/runtime/doc/vim9.txt @@ -1,4 +1,4 @@ -*vim9.txt* For Vim version 9.2. Last change: 2026 May 21 +*vim9.txt* For Vim version 9.2. Last change: 2026 Jul 05 VIM REFERENCE MANUAL by Bram Moolenaar @@ -7,8 +7,10 @@ Vim9 script commands and expressions. *Vim9* *vim9* Most expression help is in |eval.txt|. This file is about the new syntax and -features in Vim9 script. +features in Vim9 script, including more than 150 sourceable scripts. +For a short primer on Vim9 script, other resources may be helpful too, e.g., +https://learnxinyminutes.com/vim9script/. 1. What is Vim9 script? |Vim9-script| @@ -23,27 +25,28 @@ features in Vim9 script. ------------------------------------------------------------------------------ - NOTE: In this vim9.txt help file, the Vim9 script code blocks beginning - with `vim9script` (and individual lines starting with `vim9cmd`) 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!" +NOTE: In this vim9.txt help file, the Vim9 script code blocks beginning with +`vim9script` (and individual lines starting with `vim9cmd`) 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 < - 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* Vim script has been growing over time, while preserving backwards @@ -78,7 +81,7 @@ Vim9 script and legacy Vim script can be mixed. There is no requirement to rewrite old scripts, they keep working as before. You may want to use a few `:def` functions for code that needs to be fast. -:vim9[cmd] {cmd} *:vim9* *:vim9cmd* +:vim9[cmd] {cmd} *:vim9* *:vim9cmd* Evaluate and execute {cmd} using Vim9 script syntax, semantics, and behavior. Useful when typing a command, in a `:function`, or a legacy Vim script. @@ -86,61 +89,61 @@ rewrite old scripts, they keep working as before. You may want to use a few The following short example shows how a legacy Vim script command and a :vim9cmd (so Vim9 script context) may appear similar, though may differ not just syntactically, but also - semantically and behaviorally. >vim - - call popup_notification('entrée'[5:] - \ ->str2list()->string(), #{time: 7000}) - vim9cmd popup_notification('entrée'[5 :] - ->str2list()->string(), {time: 7000}) -< - Notes: 1) The reason for the different output is Vim9 script - uses character indexing whereas legacy Vim script - uses byte indexing - see |vim9-string-index|. - 2) Syntax is different too. In Vim9 script: - - The space in "[5 :]" is mandatory (see - |vim9-white-space|). - - Line continuation with "\" is not required. - - The "#" (to avoid putting quotes around dictionary - keys) is neither required nor allowed - see |#{}|. - - *E1164* + semantically and behaviorally. +>vim + call popup_notification('entrée'[5:] + \ ->str2list()->string(), #{time: 7000}) + vim9cmd popup_notification('entrée'[5 :] + ->str2list()->string(), {time: 7000}) +< + Notes: 1. The reason for the different output is Vim9 script + uses character indexing whereas legacy Vim script uses byte + indexing - see |vim9-string-index|. + 2. Syntax is different too. In Vim9 script: + - The space in "[5 :]" is mandatory (see |vim9-white-space|). + - Line continuation with "\" is not required. + - The "#" (to avoid putting quotes around dictionary keys) is + neither required nor allowed - see |#{}|. + + *E1164* `:vim9cmd` cannot stand alone; it must be followed by a command. -:leg[acy] {cmd} *:leg* *:legacy* +:leg[acy] {cmd} *:leg* *:legacy* Evaluate and execute {cmd} using legacy Vim script syntax, semantics, and behavior. It is only applicable in a Vim9 script or a `:def` function. Using an equivalent script to - the one, above (see its notes for why the output differs): >vim9 - - vim9script - # Legacy context - so, this creates a popup with [769, 101] - legacy call popup_notification('entrée'[5:] - \ ->str2list()->string(), #{time: 7000}) - # Vim9 script context - so, this creates a pop up with [101] - popup_notification('entrée'[5 :] - ->str2list()->string(), {time: 7000}) -< - Vim9 script script-local variables may be used by prefixing + the one, above (see its notes for why the output differs): +>vim9 + vim9script + # In legacy Vim script context the popup is [769, 101] + legacy call popup_notification('entrée'[5:] + \ ->str2list()->string(), #{time: 7000}) + # In Vim9 script context the popup is [101] + popup_notification('entrée'[5 :] + ->str2list()->string(), {time: 7000}) +< + Vim9 script's script-local variables may be used by prefixing "s:", like in legacy Vim script. This example shows the difference in syntax: "k" for the script-local variable in - Vim9 script, "s:k" in the legacy Vim script context. >vim9 - - vim9script - var k: string = "Okay" - echo k - legacy echo s:k -< *E1189* + Vim9 script, "s:k" in the legacy Vim script context. +>vim9 + vim9script + var k: string = "Okay" + echo k + legacy echo s:k +< + *E1189* Using `:legacy` is not allowed in compiled Vim9 script - control flow contexts. For example: >vim9 - - vim9script - def F_1189() - if v:version == 900 - # E1189: Cannot use :legacy with this command: endif - legacy endif - enddef - F_1189() -< *E1234* + control flow contexts. For example: +>vim9 + vim9script + def F_1189() + if v:version == 900 + # E1189: Cannot use :legacy with this command: endif + legacy endif + enddef + F_1189() +< *E1234* `:legacy` cannot stand alone; it must be followed by a command. @@ -1117,7 +1120,7 @@ should be used. *false* *true* *null* *null_blob* *null_channel* *null_class* *null_dict* *null_function* *null_job* *null_list* *null_object* *null_partial* *null_string* - *E1034* *E1395* + *E1034* In Vim9 script one can use the following predefined values: > true false @@ -1305,15 +1308,15 @@ Using ++var or --var in an expression is not supported yet. the function follows in the next lines, until the matching `:enddef`. *E1073* - The {name} cannot be reused at the script-local level: >vim9 - + {name} cannot be reused at the script-local level: +>vim9 vim9script def F_1073() enddef - def F_1073() # E1073: Name already defined: ... + def F_1073() # E1073: Name already defined: F… enddef < *E1011* - The {name} must be less than 100 bytes long. + {name} must be less than 100 bytes long. *E1077* {arguments} is a sequence of zero or more argument @@ -1322,71 +1325,70 @@ Using ++var or --var in an expression is not supported yet. {name} = {value} {name}: {type} = {value} The first form is a mandatory argument. So, the - declaration must provide a type. Example: >vim9 - + declaration must provide a type. Example: +>vim9 vim9script def F_1077(x): void - # E1077: Missing argument type for x + # E1077: Missing argument type for x enddef < For the second form, because the declaration does not specify it, Vim infers the type. For both second and third forms, a default {value} applies when the - caller omits it. Examples: >vim9 - + caller omits it. Examples: +>vim9 vim9script def SecondForm(arg = "Hi"): void - echo $'2. arg is a "{arg->typename()}" type ' .. - $'and the default value of arg is "{arg}"' + echo $'2. arg is a "{arg->typename()}" type ' .. + $'and the default value of arg is "{arg}"' enddef SecondForm() def ThirdForm(arg2: number = 9): void - echo $'3. default value of arg2 is {arg2}' + echo $'3. default value of arg2 is {arg2}' enddef ThirdForm() < *E1123* Arguments in a builtin function called in a `:def` - function must have commas between arguments: >vim9 - + function must have commas between arguments: +>vim9 vim9script def F_1123(a: number, b: number): void - echo max(a b) - # E1123: Missing comma before argument: b) + echo max(a b) # E1123: Missing comma before argu… enddef F_1123(1, 2) < *E1003* *E1027* *E1096* The type of value used with `:return` must match {return-type}. When {return-type} is omitted or is "void" the function is not allowed to return - anything. Examples: >vim9 - + anything. Examples: +>vim9 vim9script def F_1003(): bool - return # E1003: Missing return value + return # E1003: Missing return value enddef F_1003() < >vim9 vim9script def F_1027(): bool - echo false # E1027: Missing return statement + echo false # E1027: Missing return statement enddef F_1027() < >vim9 vim9script def F_1096(): void - return false # E1096: Returning a value ... + return false # E1096: Returning a value in a fun… enddef F_1096() < *E1056* *E1059* When ": {return-type}" is specified, {return-type} cannot be omitted (leaving a hanging colon). The ": " - also cannot be preceded by white space. Examples: >vim - + also cannot be preceded by white space. Examples: +>vim def F_1056(): - # E1056: Expected a type: + # E1056: Expected a type: enddef def F_1059() : bool - # E1059: No white space allowed before colon:... + # E1059: No white space allowed before colon: : bo… enddef < The function will be compiled into instructions when @@ -1405,21 +1407,21 @@ Using ++var or --var in an expression is not supported yet. In Vim9 script, ! is not allowed because script-local functions cannot be deleted or redefined, though they can be removed by reloading the script. Also, nested - functions cannot use ! for redefinition. Examples: >vim - + functions cannot use ! for redefinition. Examples: +>vim " Legacy Vim script :def! example def! LegacyFunc() - echo "def! is allowed in a legacy Vim script" + echo "def! is allowed in a legacy Vim script" enddef call LegacyFunc() < >vim9 vim9script - def Func() - def! InnerFunc() - # E1117: Cannot use ! with nested :def - enddef + def F1117() + def! InnerFunc() + # E1117: Cannot use ! with nested :def + enddef enddef - Func() + F1117() < >vim9 vim9script def! F_477(): void # E477: No ! allowed @@ -1437,10 +1439,11 @@ Using ++var or --var in an expression is not supported yet. reported at https://github.com/vim/vim/issues as it could represent a gap in Vim's error reporting. - *:enddef* *E1057* *E1152* *E1173* + *:enddef* + *E1057* *E1152* *E1173* :enddef End of a function defined with `:def`. It should be on - a line by itself. Examples: >vim9 - + a line by itself. Examples: +>vim9 vim9script def MyFunc() echo 'Do Something' | enddef @@ -1453,8 +1456,8 @@ Using ++var or --var in an expression is not supported yet. < >vim9 vim9script def F_1152() - function X() - enddef # E1152: Mismatched enddef + function X() + enddef # E1152: Mismatched enddef enddef < You may also find this wiki useful. It was written by an early adopter of @@ -1462,60 +1465,66 @@ Vim9 script: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md If the script the `:def` function is defined in is Vim9 script, script-local variables must be accessed without using the "s:" prefix. They must be -defined before the function is compiled and there is no way to avoid errors -(e.g., by using |exists()|) to conditionally skip undeclared variables. -For example: >vim9 - +defined before the function is compiled. Using |exists()|, which is evaluated +at runtime, cannot be used conditionally to skip undeclared variables, though +|exists_compiled()|, which is evaluated at compile time, may be used. +For example: +>vim9 vim9script def MyVim9def() - echo unus # Echoes 1 - # echo s:unus # This would be E1268 (Cannot use s: in Vim9) - if exists('duo') - # echo duo # This would be E1001 (Variable not found: duo) - endif + # echo s:one # This would be E1268 (Cannot use s: in Vim9) + if exists_compiled('one') # true at compile time + echo one # 1 + endif + if exists('two') # Evaluated at runtime + # echo two # This would be E1001 (Variable not found) + endif + if exists_compiled('two') # false at compile time + echo two + endif enddef - var unus: number = 1 - MyVim9def() # MyVim9def is compiled ("duo" does not exist yet) - var duo: number = 2 + var one: number = 1 + MyVim9def() # MyVim9def is compiled (NB: 'two' does not exist yet) + var two: number = 2 < If the script the `:def` function is defined in is legacy Vim script, script-local variables may be accessed with or without the "s:" prefix. However, using "s:" may defer variable resolution to runtime, avoiding compilation errors for variables that may not exist yet, as this example -explains: >vim - +demonstrates: +>vim " legacy Vim script def! MyLegacyDef(): void - echo [unus, s:unus] # Echoes [1, 1] - # (If uncommented) First sourcing of 'echo s:duo' is E121 and + echo [one, s:one] # [1, 1] + # (If uncommented) First sourcing of 'echo s:two' is E121 and + # causes a compilation error; subsequent sourcing echoes 2: + # echo s:two + if exists("s:two") + # First sourcing: echo is skipped; subsequent sourcing: echoes 2 + echo s:two + endif + if exists("two") + # (If uncommented) First sourcing of 'echo two' is E1001 and # causes a compilation error; subsequent sourcing echoes 2: - # echo s:duo - if exists("s:duo") - # First sourcing: skips echo; subsequent sourcing: echoes 2 - echo s:duo - endif - if exists("duo") - # (If uncommented) First sourcing of 'echo duo' is E1001 and - # causes a compilation error; subsequent sourcing echoes 2: - # echo duo - endif + # echo two + endif enddef - let s:unus = 1 - call MyLegacyDef() " Calls MyLegacyDef() and compiles if not already - let s:duo = 2 + let s:one = 1 + call MyLegacyDef() " Calls MyLegacyDef() and compiles if it isn't + let s:two = 2 < *E1269* Script-local variables in a Vim9 script must be declared at the script level. They cannot be created in a `:def` function and may not be declared -in a legacy function with the "s:" prefix. For example: >vim9 - +in a legacy function with the "s:" prefix. For example: +>vim9 vim9script function F_1269() - let s:i_wish = v:true + let s:i_wish = v:true endfunction F_1269() # E1269: Cannot create a Vim9 script variable in a function: s:i_wish < - *:defc* *:defcompile* + *:defc* *:defcompile* :defc[ompile] Compile functions and classes (|class-compile|) defined in the current script that were not compiled yet. This will report any errors found during @@ -1524,13 +1533,13 @@ in a legacy function with the "s:" prefix. For example: >vim9 Example: When the three lines (up to and including `enddef`) are sourced, there is no error because the Vim9 `:def` function is not compiled. However, if all - four lines are sourced, compilation fails: >vim9 - + four lines are sourced, compilation fails: +>vim9 vim9script def F_1027(): string enddef defcompile F_1027 # E1027: Missing return statement - +< :defc[ompile] MyClass Compile all methods in a class. (See |:disassemble| for an example.) @@ -1545,7 +1554,7 @@ in a legacy function with the "s:" prefix. For example: >vim9 {func} can also be "ClassName" to compile all functions and methods in a class. - *:disa* *:disassemble* + *:disa* *:disassemble* :disa[ssemble] {func} Show the instructions generated for {func}. This is for debugging and testing. If {func} is not found, error *E1061* occurs. @@ -1554,22 +1563,22 @@ in a legacy function with the "s:" prefix. For example: >vim9 The following example demonstrates using `:defcompile` with a |class| and `:disassemble` with a "ClassName.functionName" (positioning the cursor on - the last line of the visually sourced script): >vim9 - + the last line of the visually sourced script): +>vim9 vim9script class Line - var lnum: number - def new(this.lnum) - enddef - def SetLnum() - cursor(this.lnum, 52) - enddef + var lnum: number + def new(this.lnum) + enddef + def SetLnum() + cursor(this.lnum, 52) + enddef endclass defcompile Line disassemble Line.SetLnum var vlast: Line = Line.new(line("'>")) - vlast.SetLnum() # Cursor is positioned here->_ - + vlast.SetLnum() # Cursor is positioned here->_ +< :disa[ssemble] profile {func} Like `:disassemble` but with the instructions used for profiling. @@ -1578,104 +1587,105 @@ in a legacy function with the "s:" prefix. For example: >vim9 Like `:disassemble` but with the instructions used for debugging. - Note: For command line completion of {func}, script-local functions + Note: For command line completion of {func}, script-local functions are shown with their . Depending on options, including |wildmenumode()|, completion may work with "s:", "vim9 - +visible whereas the function-local constant "DEF_LOCAL" is not: +>vim9 vim9script const SCRIPT_LOCAL = ['A', 'script-local', 'list'] def MapList(scope: string): list - const DEF_LOCAL: list = ['A', 'def-local', 'list'] - if scope == 'script local' - return [1]->map('SCRIPT_LOCAL[v:val]') - else - return [1]->map('DEF_LOCAL[v:val]') - endif + const DEF_LOCAL: list = ['A', 'def-local', 'list'] + if scope == 'script-local' + return [1]->map('SCRIPT_LOCAL[v:val]') + else + return [1]->map('DEF_LOCAL[v:val]') + endif enddef - echo 'script local'->MapList() # Echoes ['script-local'] - echo 'def local'->MapList() # E121: Undefined variable: DEF_LOCAL + echo 'script-local'->MapList() # ['script-local'] + echo 'def-local'->MapList() # E121: Undefined variable: DEF_LOCAL < The map argument is a string expression, which is evaluated without the -function scope. Instead, in Vim9 script, use a lambda: >vim9 - +function scope. Instead, in Vim9 script, use a lambda: +>vim9 vim9script def MapList(): list - const DEF_LOCAL: list = ['A', 'def-local', 'list'] - return [1]->map((_, v) => DEF_LOCAL[v]) + const DEF_LOCAL: list = ['A', 'def-local', 'list'] + return [1]->map((_, v): string => DEF_LOCAL[v]) enddef - echo MapList() # Echoes ['def-local'] + echo MapList() # ['def-local'] < For commands that are not compiled, such as `:edit`, |backtick-expansion| can -be used and it can use the local scope. Example: >vim9 - +be used and it can use the local scope. Example: +>vim9 vim9script def EditNewBlah() - var fname: string = 'blah.txt' - split - edit `=fname` + var fname: string = 'blah.txt' + split + edit `=fname` enddef EditNewBlah() # A new split is created as buffer 'blah.txt' < + *vim9-closure* Closures defined in a loop can either share a variable or each have their own copy, depending on where the variable is declared. With a variable declared outside the loop, all closures reference the same shared variable. The following example demonstrates the consequences, with the "outloop" -variable existing only once: >vim9 - +variable existing only once: +>vim9 vim9script var flist: list def ClosureEg(n: number): void - var outloop: number = 0 # outloop is declared outside the loop! - for i in range(n) - outloop = i - flist[i] = (): number => outloop # Closures ref the same var - endfor - echo range(n)->map((i, _) => flist[i]()) + var outloop: number = 0 # outloop is declared outside the loop! + for i in range(n) + outloop = i + flist[i] = (): number => outloop # Closures ref the same var + endfor + echo range(n)->map((i, _): number => flist[i]()) enddef - ClosureEg(4) # Echoes [3, 3, 3, 3] + ClosureEg(4) # [3, 3, 3, 3] < All closures put in the list refer to the same instance, which, in the end, is 3. However, when the variable is declared inside the loop, each closure gets its -own copy, as shown in this example: >vim9 - +own copy, as shown in this example: +>vim9 vim9script var flist: list def ClosureEg(n: number): void - for i in range(n) - var inloop: number = i # inloop is declared inside the loop - flist[i] = (): number => inloop # Closures ref each inloop - endfor - echo range(n)->map((i, _) => flist[i]()) + for i in range(n) + var inloop: number = i # inloop is declared inside the loop + flist[i] = (): number => inloop # Closures ref each inloop + endfor + echo range(n)->map((i, _): number => flist[i]()) enddef - ClosureEg(4) # Echoes [0, 1, 2, 3] - + ClosureEg(4) # [0, 1, 2, 3] +< Another way to have a separate context for each closure is to call a -function to define it: >vim9 - +function to define it: +>vim9 vim9script def GetClosure(i: number): func - var infunc: number = i - return (): number => infunc + var infunc: number = i + return (): number => infunc enddef var flist: list def ClosureEg(n: number): void - for i in range(n) - flist[i] = GetClosure(i) - endfor - echo range(n)->map((i, _) => flist[i]()) + for i in range(n) + flist[i] = GetClosure(i) + endfor + echo range(n)->map((i, _): number => flist[i]()) enddef - ClosureEg(4) # Echoes [0, 1, 2, 3] + ClosureEg(4) # [0, 1, 2, 3] < *E1271* A closure must be compiled in the context that it is defined in, so that variables in that context can be found. This mostly happens correctly, @@ -1686,42 +1696,42 @@ function. In some situations, such as when a Vim9 closure which captures local variables is converted to a string and then executed, an error occurs. This happens because the string execution context cannot access the local variables from -the original context where the closure was defined. For example: >vim9 - +the original context where the closure was defined. For example: +>vim9 vim9script def F_1248(): void - var n: number - var F: func = () => { - n += 1 - } - try - execute printf("call %s()", F) - catch - echo v:exception - endtry + var n: number + var F: func = () => { + n += 1 + } + try + execute printf("call %s()", F) + catch + echo v:exception + endtry enddef F_1248() # Vim(call):E1248: Closure called from invalid context - +< In Vim9 script, a loop variable is invalid after the loop is closed. For example, this timer will echo 0 to 2 on separate lines. However, if -the variable "n" is used after the `:endfor`, that is an |E121| error: >vim9 - +the variable "n" is used after the `:endfor`, that is an |E121| error: +>vim9 vim9script for n in range(3) - var nr: number = n - timer_start(1000 * n, (_) => { - echowindow nr - }) + var nr: number = n + timer_start(1000 * n, (_) => { + echowindow nr + }) endfor try - echowindow n + echowindow n catch - echo v:exception + echo v:exception endtry < - Note: Using `:echowindow` is useful in a timer because messages go - into a popup and will not interfere with what the user is - doing when it triggers. + Note: Using `:echowindow` is useful in a timer because messages go + into a popup and will not interfere with what the user is doing when + it triggers. Converting a :function to a :def~ @@ -1741,7 +1751,8 @@ a `:def` function. The following are some of them: - If the function returns something, add the return type. (Ideally, add "void" if it does not return anything.) - Remove line continuation backslashes from places they are not required. -- Remove `let` for assigning values to |g:|, |b:|, |w:|, |t:|, or |l:| variables. +- Remove `let` for assigning values to global (|g:|), buffer (|b:|), + window (|w:|), tab (|t:|), and local (|l:|) variables. - Rewrite |lambda| expressions in Vim9 script syntax (see |vim9-lambda|). - Change comments to start with # (preceded by white space) instead of ". - Insert white space in expressions where required (see |vim9-white-space|). @@ -1749,38 +1760,38 @@ a `:def` function. The following are some of them: an |interpolated-string|.) The following legacy Vim script and Vim9 script examples demonstrate all -those differences. First, legacy Vim script: >vim - +those differences. First, legacy Vim script: +>vim let s:lnum=0 function Leg8(arg) abort - let l:pre=['Result', - \': '] - let b:arg=a:arg - let s:lnum+=2 - let b:arg*=4 - let l:result={pre->join(pre,'')}(l:pre) - return l:result.(b:arg+s:lnum)"no space before comment + let l:pre=['Result', + \': '] + let b:arg=a:arg + let s:lnum+=2 + let b:arg*=4 + let l:result={pre->join(pre,'')}(l:pre) + return l:result.(b:arg+s:lnum)"no space before comment endfunction call Leg8(10)->popup_notification(#{time: 3000})" Pops up 'Result: 42' - -The equivalent in Vim9 script: >vim9 - +< +The equivalent in Vim9 script: +>vim9 vim9script var lnum: number def Vim9(arg: number): string - final pre = ['Result', - ': '] - b:arg = arg - lnum += 2 - b:arg *= 4 - const RESULT: string = ((lpre) => join(lpre, ''))(pre) - return RESULT .. (b:arg + lnum) # space required before # comment + final pre = ['Result', + ': '] + b:arg = arg + lnum += 2 + b:arg *= 4 + const RESULT: string = ((lpre) => join(lpre, ''))(pre) + return RESULT .. (b:arg + lnum) # space required before # comment enddef - Vim9(10)->popup_notification({time: 3000}) # Pops up 'Result: 42' - -< Note: This example also demonstrates (outside the `:def` function): - - Removing "#" from the legacy |#{}| - see |vim9-literal-dict| - - Omitting `:call` (allowed, though unnecessary in Vim9 script) + Vim9(10)->popup_notification({time: 3000}) # Pops up 'Result: 42' +< + Note: This example also demonstrates (outside the `:def` function): + - Removing "#" from the legacy |#{}| - see |vim9-literal-dict|, and + - Omitting `:call` (allowed, though unnecessary in Vim9 script) Calling a :def function in an expr option ~ @@ -1789,86 +1800,87 @@ The value of a few options, such as 'foldexpr', is an expression that is evaluated to get a value. The evaluation can have quite a bit of overhead. One way to minimize the overhead, and also to keep the option value simple, is to define a compiled function and set the option to call it without -arguments. For example: >vim9 - +arguments. For example: +>vim9 vim9script def MyFoldFunc(): string - # This matches start of line (^), followed by a digit, a full stop - # a space or tab, an uppercase character, with an empty next line - return getline(v:lnum) =~ '^[[:digit:]]\.[[:blank:]][[:upper:]]' - && getline(v:lnum + 1)->empty() ? '>1' : '1' + # This matches start of line (^), followed by a digit, a full stop + # a space or tab, an uppercase character, with an empty next line + return getline(v:lnum) =~ '^[[:digit:]]\.[[:blank:]][[:upper:]]' + && getline(v:lnum + 1)->empty() ? '>1' : '1' enddef set foldexpr=MyFoldFunc() set foldmethod=expr - norm! zM + normal! zM < - Warning: This script creates and applies folds at the "Heading 1" level of - this vim9.txt help buffer. (You can use |zR|, in Normal mode, to - open all the folds after sourcing the script.) +Warning: This script creates and applies folds at the "Heading 1" level of +this vim9.txt help buffer. (You can use |zR|, in Normal mode, to open all the +folds after sourcing the script.) ============================================================================== -4. Types *vim9-types* +4. Types *vim9-types* The following types, each shown with its corresponding internal |v:t_TYPE| variable, are supported: - number |v:t_number| - string |v:t_string| - func |v:t_func| - func: {type} |v:t_func| - func({type}, ...) |v:t_func| - func({type}, ...): {type} |v:t_func| - list<{type}> |v:t_list| - dict<{type}> |v:t_dict| - float |v:t_float| - bool |v:t_bool| - none |v:t_none| - job |v:t_job| - channel |v:t_channel| - blob |v:t_blob| - class |v:t_class| - object |v:t_object| - typealias |v:t_typealias| - enum |v:t_enum| - enumvalue |v:t_enumvalue| - tuple<{type}> |v:t_tuple| - tuple<{type}, {type}, ...> |v:t_tuple| - tuple<...list<{type}>> |v:t_tuple| - tuple<{type}, ...list<{type}>> |v:t_tuple| + number |v:t_number| + string |v:t_string| + func |v:t_func| + func: {type} |v:t_func| + func({type}, ...) |v:t_func| + func({type}, ...): {type} |v:t_func| + list<{type}> |v:t_list| + dict<{type}> |v:t_dict| + float |v:t_float| + bool |v:t_bool| + none |v:t_none| + job |v:t_job| + channel |v:t_channel| + blob |v:t_blob| + class |v:t_class| + object |v:t_object| + typealias |v:t_typealias| + enum |v:t_enum| + enumvalue |v:t_enumvalue| + tuple<{type}> |v:t_tuple| + tuple<{type}, {type}, ...> |v:t_tuple| + tuple<...list<{type}>> |v:t_tuple| + tuple<{type}, ...list<{type}>> |v:t_tuple| void - *E1031* *E1186* + *E1031* *E1186* These types can be used in declarations, though no simple value can have the -"void" type. Trying to use a void as a value results in an error. Examples: >vim9 - +"void" type. Trying to use a void as a value results in an error. +Examples: +>vim9 vim9script def NoReturnValue(): void enddef try - const X: any = NoReturnValue() + const X: any = NoReturnValue() catch - echo v:exception # E1031: Cannot use void value - try - echo NoReturnValue() - catch - echo v:exception # E1186: Expression does not result in a ... - endtry + echo v:exception # E1031: Cannot use void value + try + echo NoReturnValue() + catch + echo v:exception # E1186: Expression does not result in a value… + endtry endtry < *E1008* *E1009* *E1010* *E1012* Ill-formed declarations and mismatching types result in errors. The following -are examples of errors E1008, E1009, E1010, and E1012: >vim9 - - vim9cmd var l: list - vim9cmd var l: list - vim9cmd var l: list = ['42'] +are examples of errors E1008, E1009, E1010, and E1012: +>vim9 + vim9cmd var l_1008: list + vim9cmd var l_1009: list + vim9cmd var l_1012: list = ['42'] < There is no array type. Instead, use either a list or a tuple. Those types may also be literals (constants). In the following example, [5, 6] is a list -literal and (7, ) a tuple literal. The echoed list is a list literal too: >vim9 - +literal and (7, ) a tuple literal. The echoed list is a list literal too: +>vim9 vim9script var l: list = [1, 2] var t: tuple<...list> = (3, 4) @@ -1886,111 +1898,267 @@ tuple<...list> a variadic tuple with zero or more items of tuple> a tuple with an item of type |Number| followed by zero or more items of type |String| -Examples: > - var myTuple: tuple = (20,) - var myTuple: tuple = (30, 'vim') - var myTuple: tuple = (40, 1.1, true) - var myTuple: tuple<...list> = ('a', 'b', 'c') - var myTuple: tuple> = (3, 'a', 'b', 'c') +Examples: +>vim9 + vim9script + var t1: tuple = (20,) + var t2: tuple = (30, 'vim') + var t3: tuple = (40, 1.1, true) + var t4: tuple<...list> = ('a', 'b', 'c') + var t5: tuple> = (3, 'a', 'b', 'c', 'd') < - *variadic-tuple* *E1539* + *variadic-tuple* *E1539* A variadic tuple has zero or more items of the same type. The type of a -variadic tuple must end with a list type. Examples: > - var myTuple: tuple<...list> = (1, 2, 3) - var myTuple: tuple<...list> = ('a', 'b', 'c') - var myTuple: tuple<...list> = () -< - *vim9-func-declaration* *E1005* *E1007* - *vim9-partial-declaration* - *vim9-func-type* -A function (or partial) may be declared in the following ways: -func any kind of function reference, no type - checking for arguments or return value -func: void any number and type of arguments, no return - value -func: {type} any number and type of arguments with specific - return type - -func() function with no argument, does not return a - value -func(): void same -func(): {type} function with no argument and return type - -func({type}) function with argument type, does not return - a value -func({type}): {type} function with argument type and return type -func(?{type}) function with type of optional argument, does - not return a value -func(...list<{type}>) function with type of list for variable number - of arguments, does not return a value -func({type}, ?{type}, ...list<{type}>): {type} - function with: - - type of mandatory argument - - type of optional argument - - type of list for variable number of - arguments - - return type - -If the return type is "void" the function does not return a value. - +variadic tuple must end with a list type otherwise E1539 is given. Examples: +>vim9 + vim9script + var t6: tuple<...list> = (1, 2, 3) + var t7: tuple<...list> = ('a', 'b', 'c') + var t8: tuple<...list> = () + var tE: tuple<...any> = () # E1539: Variadic tuple must end with list… +< + *vim9-func-declaration* + *vim9-partial-declaration* + *vim9-func-type* +A function (or partial) may be declared in many ways, including: +> + func + func: void + func: {type} + func()[: void] + func(): {type} + func({type})[: void] + func({type}): {type} + func(?{type})[: void] + func(?{type}): {type} + func(...list<{type}>)[: void] + func(...list<{type}>): {type} + func({type}, ...list<{type}>)[: void] + func({type}, ...list<{type}>): {type} +< +If the return type is "void", the function does not return a value. All the +ways listed above are explained and demonstrated in the following examples, +all of which echo '9'. + +func + - any kind of function reference + - any type of return value or no return value +>vim9 + vim9script + const I = (n) => n + 1 + const F: func = I->function() + echo F(8) +< +func: void + - any number and type of arguments + - does not return a value +>vim9 + vim9script + def I(n: number): void + echo n + 1 + enddef + const F: func: void = I->function() + F(8) +< +func: {type} + - any number and type of arguments + - returns a typed value +>vim9 + vim9script + const I = (n): number => n + 1 + const F: func: number = I->function() + echo F(8) +< +func()[: void] + - no arguments + - does not return a value +>vim9 + vim9script + var n: number = 8 + def I(): void + echo n + 1 + enddef + const F: func(): void = I->function() + F() +< +func(): {type} + - no arguments + - returns a typed value +>vim9 + vim9script + var n: number = 8 + def I(): number + return n + 1 + enddef + const F: func(): number = I->function() + echo F() +< +func({type})[: void] + - typed argument + - does not return a value +>vim9 + vim9script + def I(n: number): void + echo n + 1 + enddef + const F: func(number): void = I->function() + F(8) +< +func({type}): {type} + - typed argument + - returns a typed value +>vim9 + vim9script + const I = (n: number): number => n + 1 + const F: func(number): number = I->function() + echo F(8) +< +func(?{type})[: void] + - optional typed argument + - does not return a value +>vim9 + vim9script + def I(n: number = 8): void + echo n + 1 + enddef + const F: func(?number): void = I->function() + F() +< +func(?{type}): {type} + - optional typed argument + - returns a typed value +>vim9 + vim9script + def I(n: number = 8): number + return n + 1 + enddef + const F: func(?number): number = I->function() + echo F() +< +func(...list<{type}>)[: void] + - typed list for variable number of arguments + - does not return a value +>vim9 + vim9script + def L(...l: list): void + echo l->reduce((x, y) => x + y) + enddef + const F: func(...list): void = L->function() + F(8, 0, 0, 1) +< +func(...list<{type}>): {type} + - typed list for variable number of arguments + - returns a typed value +>vim9 + vim9script + def L(...l: list): number + return l->reduce((x, y) => x + y) + enddef + const F: func(...list): number = L->function() + echo F(8, 0, 0, 1) +< +func({type}, ...list<{type}>)[: void] + - typed mandatory argument + - typed list for variable number of arguments + - does not return a value +>vim9 + vim9script + def D(a: float, ...l: list): void + echo (a + l->reduce((x, y) => x + y))->float2nr() + enddef + const F: func(float, ...list): void = D->function() + F(8.0, 0, 0, 1) +< +func({type}, ...list<{type}>): {type} + - typed mandatory argument + - typed list for variable number of arguments + - returns a typed value +>vim9 + vim9script + def D(a: float, ...l: list): number + return (a + l->reduce((x, y) => x + y))->float2nr() + enddef + const F: func(float, ...list): number = D->function() + echo F(8.0, 0, 0, 1) +< 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. This interactive example -prompts for a circle's radius and returns its area to two decimal places, -using a partial: >vim9 - +prompts for a circle's radius and returns its area using a partial: +>vim9 vim9script def CircleArea(pi: float, radius: float): float - return pi * radius->pow(2) + return pi * radius->pow(2) enddef const AREA: func(float): float = CircleArea->function([3.14]) const RADIUS: float = "Enter a radius value: "->input()->str2float() echo $"\nThe area of a circle with a radius of {RADIUS} is " .. - $"{AREA(RADIUS)} (π to two d.p.)" + $"{AREA(RADIUS)} (π to two d.p.)" +< + *E1005* +No more than 19 argument types may be used in a Funcref (one less than what is +allowed in a function, including a `:def` or lambda - see |E740|). Twenty or +more argument types gives E1005, as this example shows: +>vim9 + vim9script + var X: func + X = (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t) => 0 + var F: func(any, any, any, any, any, any, any, any, any, any, any, + \ any, any, any, any, any, any, any, any, any): any = X->function() + # E1005: Too many argument types +< + *E1007* +A mandatory argument may not come after an optional argument in a Funcref: +>vim9 + vim9script + def I(n: number, f: float): void + echo n + f + enddef + const F: func(?number, float) = I->function() + # E1007: Mandatory argument after optional argument < - *vim9-typealias-type* + *vim9-typealias-type* Custom types (|typealias|) can be defined with `:type`. They must start with a capital letter (which avoids name clashes with either current or future builtin types) similar to user functions. This example creates a list of -perfect squares and reports on |type()| (14, a typealias) and the |typename()|: >vim9 - +perfect squares, reporting on |type()| (14, a typealias) and the |typename()|: +>vim9 vim9script type Ln = list final perfect_squares: Ln = [1, 4, 9, 16, 25] echo "Typename (Ln): " .. - $"type() is {Ln->type()} and typename() is {Ln->typename()}" + $"type() is {Ln->type()} and typename() is {Ln->typename()}" < - *E1105* -A typealias itself cannot be converted to a string: >vim9 - + *E1105* +A typealias itself cannot be converted to a string: +>vim9 vim9script type Ln = list const FAILS: func = (): string => { - echo $"{Ln}" # E1105: Cannot convert typealias to string - } -< - *vim9-class-type* *vim9-interface-type* + echo $"{Ln}" # E1105: Cannot convert typealias to string + } +< *vim9-class-type* *vim9-interface-type* *vim9-object-type* A |class|, |object|, and |interface| may all be used as types. The following interactive example prompts for a float value and returns the area of two different shapes. It also reports on the |type()| and |typename()| of the -classes, objects, and interface: >vim9 - +classes, objects, and interface: +>vim9 vim9script interface Shape - def InfoArea(): tuple + def InfoArea(): tuple endinterface class Circle implements Shape - var radius: float - def InfoArea(): tuple - return ('Circle (π × r²)', 3.141593 * this.radius->pow(2)) - enddef + var radius: float + def InfoArea(): tuple + return ('Circle (π × r²)', 3.141593 * this.radius->pow(2)) + enddef endclass class Square implements Shape - var side: float - def InfoArea(): tuple - return ('Square (s²)', this.side->pow(2)) - enddef + var side: float + def InfoArea(): tuple + return ('Square (s²)', this.side->pow(2)) + enddef endclass const INPUT: float = "Enter a float value: "->input()->str2float() echo "\nAreas of shapes:" @@ -1998,8 +2166,8 @@ classes, objects, and interface: >vim9 var mySquare: object = Square.new(INPUT) final shapes: list = [myCircle, mySquare] for shape in shapes - const [N: string, A: float] = shape.InfoArea() - echo $"\t- {N} has area of {A}" + const [N: string, A: float] = shape.InfoArea() + echo $"\t- {N} has area of {A}" endfor echo "\n\t\ttype()\ttypename()\n\t\t------\t----------" echo $"Circle\t\t{Circle->type()}\t{Circle->typename()}" @@ -2013,19 +2181,19 @@ classes, objects, and interface: >vim9 An |enum| may be used as a type (|v:t_enum|). Variables holding enum values have the enumvalue type (|v:t_enumvalue|) at runtime. The following interactive example prompts for a character and returns information about -either a square or a rhombus. It also reports on the |type()| and |typename()| -of the enum and enumvalue: >vim9 - +either a square or a rhombus. It also reports on the |type()| and the +|typename()| of the enum and enumvalue: +>vim9 vim9script enum Quad - Square('four', 'only'), - Rhombus('opposite', 'no') - var eq: string - var ra: string - def string(): string - return $"\nA {this.name} has " .. - $"{this.eq} equal sides and {this.ra} right angles\n\n" - enddef + Square('four', 'only'), + Rhombus('opposite', 'no') + var eq: string + var ra: string + def string(): string + return $"\nA {this.name} has " .. + $"{this.eq} equal sides and {this.ra} right angles\n\n" + enddef endenum echo "Rhombus (r) or Square (s)?" var myQuad: Quad = getcharstr() =~ '\c^R' ? Quad.Rhombus : Quad.Square @@ -2033,10 +2201,10 @@ of the enum and enumvalue: >vim9 echo $"Quad \t{Quad->type()} \t{Quad->typename()}" echo $"myQuad\t{myQuad->type()}\t{myQuad->typename()}" < - Notes: This script uses builtin method "string()" (|object-string()|). - The typename() of Quad and myQuad are the same ("enum") - whereas the type() is distinguished (myQuad returns 16, - enumvalue, whereas Quad returns 15, enum). + Notes: This uses builtin method "string()" - see |object-string()|. + The typename() of Quad and myQuad are the same ("enum") whereas + the type() is distinguished (myQuad returns 16, which is an EnumValue, + whereas Quad returns 15, which is an Enum). Variable types and type casting ~ *variable-types* @@ -2066,33 +2234,33 @@ The difference is demonstrated in the following example. With funcref variable "NTC", Vim infers the expression type "[1, b:two]" as list, then verifies whether it can be assigned to the list return type. With funcref variable "TC", the type cast means Vim first checks whether "b:two" is -a type: >vim9 - +a type: +>vim9 vim9script b:two = '2' const NTC: func = (): list => { - return [1, b:two] - } + return [1, b:two] + } disassemble NTC # 3 CHECKTYPE list stack [-1] try - NTC() + NTC() catch - echo v:exception .. "\n\n" # expected list but... + echo v:exception .. "\n\n" # E1012: Type mismatch; Expected list => { - return [1, b:two] - } + return [1, b:two] + } disassemble TC # 2 CHECKTYPE number stack [-1] try - TC() + TC() catch - echo v:exception # expected number but got string + echo v:exception # E1012: Type mismatch; Expected number but got s… endtry < - Note: Notice how the error messages differ, showing when - type checking occurs. + Note: Notice how the error messages differ, showing when type checking + occurs. - *E1104* + *E1104* The syntax of a type cast is "<{type}>". An error occurs if either the opening "<" (|E121|) or closing ">" (E1104) is omitted. Also, white space is not allowed either after the "<" (|E15|) or before the ">" (|E1068|), which @@ -2104,8 +2272,8 @@ function such as |string()| to convert to a string, or |str2nr()| to convert a string to a number. If type casting is applied to a chained expression, it must be compatible with -the final result. Examples: >vim9 - +the final result. Examples: +>vim9 vim9script # These type casts work echo >[3, 2, 1]->extend(['Go!']) @@ -2114,24 +2282,31 @@ the final result. Examples: >vim9 # This type cast fails echo [3, 2, 1]->extend(['Go!'])->string() < - *E1272* + *E1272* If a type is used in a context where types are not expected you can get E1272. For example: > :vim9cmd echo islocked('x: string') -< Note: This must be executed from Vim's command line, not sourced. +< Note: To see the error, this command must be executed from Vim's command + line, not sourced. - *E1363* + *E1363* *E1395* If a type is incomplete, such as when an object's class is unknown, E1363 -results. For example: >vim9 - +or E1395 are given. Examples: +>vim9 vim9script - var E1363 = null_class.member # E1363: Incomplete type + var E1363 = null_class.member # E1363: Incomplete type +< >vim9 + vim9script + def F1395(): void + echo null_class.member + enddef + F1395() # E1395: Using a null class < -Another null object-related error is |E1360|: >vim9 - +Another null object-related error is |E1360|: +>vim9 vim9script var obj = null_object - var E1360 = obj.MyMethod() # E1360: Using a null object + var E1360 = obj.MyMethod() # E1360: Using a null object < Type inference ~ @@ -2140,32 +2315,32 @@ Declaring types explicitly provides many benefits, including targeted type checking and clearer error messages. Nonetheless, Vim often can infer types automatically when they are omitted. For example, each of these variables' types are inferred, with the |type()| and |typename()| echoed showing those -inferred types: >vim9 - +inferred types: +>vim9 vim9script echo "\t type()\t typename()" - var b = true | echo $"{b} \t {b->type()} \t {b->typename()}" - var f = 4.2 | echo $"{f} \t {f->type()} \t {f->typename()}" - var l = [1, 2] | echo $"{l} \t {l->type()} \t {l->typename()}" - var n = 42 | echo $"{n} \t {n->type()} \t {n->typename()}" - var s = 'yes' | echo $"{s} \t {s->type()} \t {s->typename()}" - var t = (42, ) | echo $"{t} \t {t->type()} \t {t->typename()}" + var b = true | echo $"{b} \t {b->type()} \t {b->typename()}" + var f = 4.2 | echo $"{f} \t {f->type()} \t {f->typename()}" + var l = [1, 2] | echo $"{l} \t {l->type()} \t {l->typename()}" + var n = 42 | echo $"{n} \t {n->type()} \t {n->typename()}" + var s = 'yes' | echo $"{s} \t {s->type()} \t {s->typename()}" + var t = (42, ) | echo $"{t} \t {t->type()} \t {t->typename()}" < The type of a list, tuple, or dictionary is inferred from the common type of its values. When the values are all the same type, that type is used. If there is a mix of types, the "any" type is used. In the following example, -the echoed |typename()| for each literal demonstrates these points: >vim9 - +the echoed |typename()| for each literal demonstrates these points: +>vim9 vim9script - echo [1, 2]->typename() # list - echo [1, 'x']->typename() # list - echo {ints: [1, 2], bools: [false]}->typename() # dict> - echo (true, false)->typename() # tuple + echo [1, 2]->typename() # list + echo [1, 'x']->typename() # list + echo {ints: [1, 2], bools: [false]}->typename() # dict> + echo (true, false)->typename() # tuple < The common type of function references, when they do not all have the same number of arguments, is indicated with "(...)", meaning the number of -arguments is unequal. This script demonstrates a "list": >vim9 - +arguments is unequal. This script demonstrates a "list": +>vim9 vim9script def Foo(x: bool): void enddef @@ -2175,12 +2350,12 @@ arguments is unequal. This script demonstrates a "list": >vim9 echo funclist->typename() < Script-local variables in a Vim9 script are type checked. The type is -also checked for variables declared in a legacy function. For example: >vim9 - +also checked for variables declared in a legacy function. For example: +>vim9 vim9script var my_local = (1, 2) function Legacy() - let b:legacy = [1, 2] + let b:legacy = [1, 2] endfunction Legacy() echo $"{my_local} is type {my_local->type()} ({my_local->typename()})" @@ -2190,24 +2365,24 @@ also checked for variables declared in a legacy function. For example: >vim9 When a type is declared for a List, Tuple, or Dictionary, the type is attached to it. Similarly, if a type is not declared, the type Vim infers is attached. In either case, if an expression attempts to change the type, E1013 results. -This example has its type inferred and demonstrates E1013: >vim9 - +This example has its type inferred and demonstrates E1013: +>vim9 vim9script var lb = [true, true] # Two bools, so Vim infers list type - echo lb->typename() # Echoes list - lb->extend([0]) # E1013 Argument 2: type mismatch, ... + echo lb->typename() # list + lb->extend([0]) # E1013 Argument 2: type mismatch, … < If you want a permissive list, either explicitly use or declare an -empty list initially (or both, i.e., `list = []`). Examples: >vim9 - +empty list initially (or both, i.e., `list = []`). Examples: +>vim9 vim9script final la: list = [] echo la->extend(['two', 1]) final le = [] echo le->extend(la) < -Similarly for a permissive dictionary: >vim9 - +Similarly for a permissive dictionary: +>vim9 vim9script final da: dict = {} echo da->extend({2: 2, 1: 'One'}) @@ -2215,8 +2390,8 @@ Similarly for a permissive dictionary: >vim9 echo de->extend(da)->string() < And, although tuples themselves are immutable, permissive tuple concatenation -can be achieved with either "any" or an empty tuple: >vim9 - +can be achieved with either "any" or an empty tuple: +>vim9 vim9script var t_any: tuple<...list> = (3, '2') t_any = t_any + (true, ) @@ -2226,155 +2401,158 @@ can be achieved with either "any" or an empty tuple: >vim9 echo t_dec_empty < If a list literal or dictionary literal is not bound to a variable, its type -may change, as this example shows: >vim9 - +may change, as this example shows: +>vim9 vim9script - echo [3, 2, 1]->typename() # list - echo [3, 2, 1]->extend(['Zero'])->typename() # list - echo {1: ['One']}->typename() # dict> - echo {1: ['One']}->extend({2: [2]})->typename() # dict> + echo [3, 2, 1]->typename() # list + echo [3, 2, 1]->extend(['Zero'])->typename() # list + echo {1: ['One']}->typename() # dict> + echo {1: ['One']}->extend({2: [2]})->typename() # dict> < Stricter type checking ~ - *type-checking* + *type-checking* 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 (and no error message) if the string doesn't start with a number. Quite often this leads to hard-to-find -bugs. For example, in legacy Vim script this echoes "1": >vim - +bugs. For example, in legacy Vim script this echoes "1": +>vim echo 123 == '123' < -However, if an unintended space is included, "0" is echoed: >vim - +However, if an unintended space is included, "0" is echoed: +>vim echo 123 == ' 123' < - *E1206* + *E1206* In Vim9 script this has been made stricter. In most places it works just as before if the value used matches the expected type. For example, in both legacy Vim script and Vim9 script trying to use anything other than a -dictionary when it is required: >vim - +dictionary when it is required: +>vim echo [8, 9]->keys() - vim9cmd echo [8, 9]->keys() # E1206: Dictionary required + vim9cmd echo [8, 9]->keys() # E1206: Dictionary required < - *E1023* *E1024* *E1029* *E1030* - *E1174* *E1175* *E1210* *E1212* + *E1023* *E1024* *E1029* + *E1030* *E1174* *E1175* + *E1210* *E1212* However, sometimes there will be an error in Vim9 script, which breaks backwards compatibility. The following examples illustrate various places this happens. The legacy Vim script behavior, which does not fail, is shown first. It is followed by the error that occurs if the same command is used in Vim9 script. - - Using a number (except 0 or 1) where a bool is expected: >vim - +- Using a number (except 0 or 1) where a bool is expected: +>vim echo v:version ? v:true : v:false - vim9cmd echo v:version ? true : false # E1023: Using a Number as a... + vim9cmd echo v:version ? true : false # E1023: Using a Number as a… < - - Using a number where a string is expected: >vim - +- Using a number where a string is expected: +>vim echo filter([1, 2], 0) - vim9cmd echo filter([1, 2], 0) # E1024: Using a Number as a String + vim9cmd echo filter([1, 2], 0) # E1024: Using a Number as a String < - - Not using a number where a number is expected: >vim - - " In this example, Vim script treats v:false as 0 +- Not using a number where a number is expected: +>vim + " In this example, legacy Vim script treats v:false as 0 function Not1029() - let b:l = [42] | unlet b:l[v:false] + let b:l = [42] | unlet b:l[v:false] endfunction call Not1029() | echo b:l < >vim9 vim9script def E1029(): void - b:l = [42] | unlet b:l[false] + b:l = [42] | unlet b:l[false] enddef E1029() # E1029: Expected number but got bool < - - Using a string as a number: >vim - +- Using a string as a number: +>vim let b:l = [42] | unlet b:l['#'] | echo b:l - vim9cmd b:l = [42] | vim9cmd unlet b:l['#'] # E1030: Using a string... + vim9cmd b:l = [42] | vim9cmd unlet b:l['#'] # E1030: Using a string < - - Not using a string where an argument requires a string: >vim9 - +- Not using a string where an argument requires a string: +>vim9 echo substitute('Hallo', 'a', 'e', v:true) - vim9cmd echo substitute('Hallo', 'a', 'e', true) # E1174: String... + vim9cmd echo substitute('Hallo', 'a', 'e', true) # E1174: String… < - - Using an empty string in an argument that requires a non-empty string: >vim9 - +- Using an empty string in an argument that requires a non-empty string: +>vim9 echo exepath('') - vim9cmd echo exepath('') # E1175: Non-empty string required for arg... + vim9cmd echo exepath('') # E1175: Non-empty string required for argu… < - - Not using a number when it is required: >vim - +- Not using a number when it is required: +>vim echo gettabinfo('a') vim9cmd echo gettabinfo('a') # E1210: Number required for argument 1 < - - Not using a bool when it is required: >vim - +- Not using a bool when it is required: +>vim echo char2nr('¡', 2) vim9cmd echo char2nr('¡', 2) # E1212: Bool required for argument 2 < - - Not using a number when a number is required (|E521|): >vim - +- Not using a number when a number is required (|E521|): +>vim let &laststatus='2' vim9cmd &laststatus = '2' < - - Not using a string when a string is required (|E928|): >vim - +- Not using a string when a string is required (|E928|): +>vim let &langmenu = 42 vim9cmd &langmenu = 42 # E928: String required < - - Comparing a |Special| with 'is' fails in some instances (|E1037|, |E1072|): >vim - - " 1 is echoed because these are both true +- Comparing a |Special| with "is" often fails (|E1037|, |E1072|): +>vim + " 1 is echoed because these are both true in legacy Vim script echo v:null is v:null && v:none is v:none - " 0 is echoed because all these expressions are false + " Similarly, 0 is echoed for these (false in legacy Vim script) echo v:none is v:null || v:none is 8 || v:true is v:none " All these are errors in Vim9 script - vim9cmd echo v:null is v:null # E1037: Cannot use 'is' with special - vim9cmd echo v:none is v:none # E1037: Cannot use 'is' with special - vim9cmd echo v:none is v:null # E1037: Cannot use 'is' with special - vim9cmd echo v:none is 8 # E1072: Cannot compare special with numb - vim9cmd echo v:true is v:none # E1072: Cannot compare bool with special -< - Note: Although the last two Vim9 script examples above error using - `v:none`, they return `false` using `null` (which is the same - as `v:null` - see |v:null|): >vim9 - - vim9script - echo null is 8 # false - echo true is null # false -< - - Using a string where a bool is required (|E1135|): >vim - + vim9cmd echo v:null is v:null # E1037: Cannot use 'is' with special + vim9cmd echo v:none is v:none # E1037: Cannot use 'is' with special + vim9cmd echo v:none is v:null # E1037: Cannot use 'is' with special + vim9cmd echo v:none is 8 # E1072: Cannot compare special with … + vim9cmd echo v:true is v:none # E1072: Cannot compare bool with spe… +< + Note: Although the last two Vim9 script examples above error with + `v:none`, they return `false` with `v:null` (which is the same as + `null` - see |v:null|): +>vim9 + vim9script + echo v:null is 8 # false + echo true is v:null # false +< +- Using a string where a bool is required (|E1135|): +>vim echo '42' ? v:true : v:false vim9cmd echo '42' ? true : false # E1135: Using a String as a Bool < - - Using a bool as a number (|E1138|): >vim - +- Using a bool as a number (|E1138|): +>vim let &laststatus=v:true vim9cmd &laststatus = true < One consequence is that the item type of a list or dict given to |map()| must not change when its type is either declared or inferred. For example, this -gives an error in Vim9 script, whereas in legacy Vim script it is allowed: >vim - - " legacy Vim script changes s:mylist to ['item 0', 'item 1'] +list's type is changed successfully in legacy Vim script: +>vim + " legacy Vim script changing list [0, 1] to ['item 0', 'item 1'] let s:mylist = [0, 1] call map(s:mylist, {i -> $"item {i}"}) echo s:mylist -< >vim9 +< +whereas in Vim9 script it gives |E1012|: +>vim9 vim9script - var mylist = [0, 1] # Vim infers mylist is list - map(mylist, (i, _) => $"item {i}") # E1012: type mismatch... + var mylist = [0, 1] # Vim infers mylist is list + map(mylist, (i, _) => $"item {i}") # E1012: Type mismatch; expected n… < The error occurs because `map()` tries to modify the list elements to strings, which conflicts with the declared type. -Use |mapnew()| instead. It creates a new list, and Vim infers its type if it -is not specified. Inferred and declared types are shown in this example: >vim9 - +Use |mapnew()| instead. It creates a new list, and Vim infers its type when it +is not specified. Inferred and declared types are shown in this example: +>vim9 vim9script var mylist = [0, 1] var infer = mylist->mapnew((i, _) => $"item {i}") @@ -2385,10 +2563,10 @@ is not specified. Inferred and declared types are shown in this example: >vim9 The key concept here is, variables with declared or inferred types cannot have the types of the elements within their containers change. However, type "changes" are allowed for either: - - a container literal (not bound to a variable), or - - a container where |copy()| or |deepcopy()| is used in method chaining. -Both are demonstrated in this example: >vim9 - +- a container literal (not bound to a variable), or +- a container where |copy()| or |deepcopy()| is used in method chaining. +Both are demonstrated in this example: +>vim9 vim9script # list literal echo [1, 2]->map((_, v) => $"#{v}") @@ -2407,10 +2585,10 @@ not needed because the original list is unchanged (as "echo mylist" shows, above). If the item type was not declared or determined to be "", it will not -change, even if all items later become the same type. However, when `mapnew()` -is used, inference means that the new list will reflect the type(s) present. -For example: >vim9 - +change, even if all items later become the same type. However, when +`mapnew()` is used, inference means that the new list will reflect the type(s) +present. For example: +>vim9 vim9script # list var mylist = [1, '2'] # mixed types, i.e., list @@ -2422,87 +2600,87 @@ For example: >vim9 echo (newlist, newlist->typename()) # newlist is a list < Using |extend()| and |extendnew()| is similar, i.e., a list literal may use -the former, so, this is okay: >vim9 - +the former, so, this is okay: +>vim9 vim9cmd echo [1, 2]->extend(['3']) # [1, 2, 3] < -whereas, this is not: >vim9 - +whereas, this is not: +>vim9 vim9script var mylist: list = [1, 2] - echo mylist->extend(['3']) # E1013: Argument 2: type mismatch + echo mylist->extend(['3']) # E1013: Argument 2: type mismatch < Using |extendnew()| is needed for extending an existing typed list, except where the extension matches the list's type (or it is "any"). For example, first extending with an element of the same type, then extending with a -different type: >vim9 - +different type: +>vim9 vim9script var mylist: list = [1, 2] mylist->extend([3]) - echo mylist->extendnew(['4']) # [1, 2, 3, '4'] - -< *E1158* + echo mylist->extendnew(['4']) # [1, 2, 3, '4'] +< + *E1158* Using |flatten()| is not allowed in Vim9 script, because it is intended always to change the type. This even applies to a list literal -(unlike |map()| and |extend()|). Instead, use |flattennew()|: >vim9 - +(unlike |map()| and |extend()|). Instead, use |flattennew()|: +>vim9 vim9cmd [1, [2, 3]]->flatten() # E1158: Cannot use flatten vim9cmd echo [1, [2, 3]]->flattennew() # [1, 2, 3] < Assigning to a funcref with specified arguments (see |vim9-func-declaration|) -involves strict type checking of the arguments. For example, this works: >vim9 - +involves strict type checking of the arguments. For example, this works: +>vim9 vim9script var F_name_age: func(string, number): string F_name_age = (n: string, a: number): string => $"Name: {n}, Age: {a}" echo F_name_age('Bob', 42) < -whereas this fails with error |E1012| (type mismatch): >vim9 - +whereas this fails with error |E1012| (type mismatch): +>vim9 vim9script var F_name_age: func(string, number): string F_name_age = (n: string, a: string): string => $"Name: {n}, Age: {a}" < If there is a variable number of arguments they must have the same type, as in -this example: >vim9 - +this example: +>vim9 vim9script var Fproduct: func(...list): number Fproduct = (...v: list): number => reduce(v, (a, b) => a * b) - echo Fproduct(3, 2, 4) # Echoes 24 + echo Fproduct(3, 2, 4) # 24 < -And may be used to accommodate mixed types: >vim9 - +And may be used to accommodate mixed types: +>vim9 vim9script var FlatSort: func(...list): any FlatSort = (...v: list) => flattennew(v)->sort('n') - echo FlatSort(true, [[[5, 3], 2], 4]) # Echoes [true, 2, 3, 4, 5] -< - Note: Using in a lambda does not avoid type checking of the - funcref. It remains constrained by the declared funcref's - type and, as these examples show, a runtime or compiling error - occurs when the types mismatch: >vim9 - - vim9script - var FuncSN: func(string): number - FuncSN = (v: any): number => v->str2nr() - echo FuncSN('162')->nr2char() # Echoes ¢ - echo FuncSN(162)->nr2char()) # E1013 (runtime error) + echo FlatSort(true, [[[5, 3], 2], 4]) # [true, 2, 3, 4, 5] +< + Note: Using in a lambda does not avoid type checking of the + funcref. It remains constrained by the declared funcref's type and, + as these examples show, a runtime or compiling error occurs when the + types mismatch: +>vim9 + vim9script + var FuncSN: func(string): number + FuncSN = (v: any): number => v->str2nr() + echo FuncSN('162')->nr2char() # ¢ + echo FuncSN(162)->nr2char() # E1013 (a runtime error) < >vim9 - vim9script - var FuncSN: func(string): number - FuncSN = (v: any): number => v->str2nr() - def FuncSNfail(): void - echo FuncSN('162')->nr2char() # No echo because ... - echo FuncSN(162)->nr2char() # Error while compiling - enddef - FuncSNfail() + vim9script + var FuncSN: func(string): number + FuncSN = (v: any): number => v->str2nr() + def FuncSNfail(): void + echo FuncSN('162')->nr2char() # No echo because ... + echo FuncSN(162)->nr2char() # E1013 (now, a compiling error) + enddef + FuncSNfail() < When the funcref has no arguments specified, there is no type checking. This example shows FlexArgs has a string argument the first time and a list the -following time: >vim9 - +following time: +>vim9 vim9script var FlexArgs: func: string FlexArgs = (s: string): string => $"It's countdown time {s}..." @@ -2510,40 +2688,40 @@ following time: >vim9 FlexArgs = (...values: list): string => join(values, ', ') echo FlexArgs('3', '2', '1', 'GO!') < - *E1211* *E1217* *E1218* *E1219* *E1220* - *E1221* *E1222* *E1223* *E1224* *E1225* - *E1226* *E1228* *E1235* *E1238* *E1251* - *E1253* *E1256* *E1297* *E1298* *E1301* - *E1528* *E1529* *E1530* *E1531* *E1534* + *E1211* *E1217* *E1218* *E1219* *E1220* + *E1221* *E1222* *E1223* *E1224* *E1225* + *E1226* *E1228* *E1235* *E1238* *E1251* + *E1253* *E1256* *E1297* *E1298* *E1301* + *E1528* *E1529* *E1530* *E1531* *E1534* Types are checked for most builtin functions to make it easier to spot mistakes. The following one-line |:vim9| commands, calling builtin functions, -demonstrate many of those type-checking errors: >vim9 - - vim9 9->list2blob() # E1211: List required for argume... - vim9 9->ch_close() # E1217: Channel or Job required ... - vim9 9->job_info() # E1218: Job required for argumen... - vim9 [9]->cos() # E1219: Float or Number required... - vim9 {}->remove([]) # E1220: String or Number require... - vim9 null_channel->ch_evalraw(9) # E1221: String or Blob required ... - vim9 9->col() # E1222: String or List required ... - vim9 9->complete_add() # E1223: String or Dictionary req... - vim9 setbufline(9, 9, {}) # E1224: String, Number or List r... - vim9 9->count(9) # E1225: String, List, Tuple or D... - vim9 9->add(9) # E1226: List or Blob required fo... - vim9 9->remove(9) # E1228: List, Dictionary, or Blo... - vim9 getcharstr('9') # E1235: Bool or number required ... - vim9 9->blob2list() # E1238: Blob required for argume... - vim9 9->filter(9) # E1251: List, Tuple, Dictionary,... - vim9 9->reverse() # E1253: String, List, Tuple or B... - vim9 9->call(9) # E1256: String or Function requi... - vim9 null_dict->winrestview() # E1297: Non-NULL Dictionary requ... - vim9 {}->prop_add_list(null_list) # E1298: Non-NULL List required f... - vim9 {}->repeat(9) # E1301: String, Number, List, Tu... - vim9 9->index(9) # E1528: List or Tuple or Blob re... - vim9 9->join() # E1529: List or Tuple required f... - vim9 9->max() # E1530: List or Tuple or Diction... - vim9 9->get(9) # E1531: Argument of get() must b... - vim9 9->tuple2list() # E1534: Tuple required for argum... +demonstrate many of those type-checking errors: +>vim9 + vim9 9->list2blob() # E1211: List required for argument… + vim9 9->ch_close() # E1217: Channel or Job required fo… + vim9 9->job_info() # E1218: Job required for argument … + vim9 [9]->cos() # E1219: Float or Number required f… + vim9 {}->remove([]) # E1220: String or Number required … + vim9 null_channel->ch_evalraw(9) # E1221: String or Blob required fo… + vim9 9->col() # E1222: String or List required fo… + vim9 9->complete_add() # E1223: String or Dictionary requi… + vim9 setbufline(9, 9, {}) # E1224: String, Number or List req… + vim9 9->count(9) # E1225: String, List, Tuple or Dic… + vim9 9->add(9) # E1226: List or Blob required for … + vim9 9->remove(9) # E1228: List, Dictionary, or Blob … + vim9 getcharstr('9') # E1235: Bool or number required fo… + vim9 9->blob2list() # E1238: Blob required for argument… + vim9 9->filter(9) # E1251: List, Tuple, Dictionary, B… + vim9 9->reverse() # E1253: String, List, Tuple or Blo… + vim9 9->call(9) # E1256: String or Function require… + vim9 null_dict->winrestview() # E1297: Non-NULL Dictionary requir… + vim9 {}->prop_add_list(null_list) # E1298: Non-NULL List required for… + vim9 {}->repeat(9) # E1301: String, Number, List, Tupe… + vim9 9->index(9) # E1528: List or Tuple or Blob requ… + vim9 9->join() # E1529: List or Tuple required for… + vim9 9->max() # E1530: List or Tuple or Dictionar… + vim9 9->get(9) # E1531: Argument of get() must be … + vim9 9->tuple2list() # E1534: Tuple required for argumen… < Reserved for future use: *E1227* *E1250* *E1252* E1227: List or Dictionary required for argument %d @@ -2562,54 +2740,56 @@ When declaring a variable without an initializer, an explicit type must be provided. Each category has different default initialization semantics. Primitives default to type-specific values. All primitives are empty but do -not equal `null`: >vim9 - - vim9script - var n: number | echo [n, n->empty(), n == null] # [0, 1, false] - var f: float | echo [f, f->empty(), f == null] # [0.0, 1, false] - var b: bool | echo [b, b->empty(), b == null] # [false, 1, false] -< -Containers default to an empty container. Only an empty string equals `null`: >vim9 - - vim9script - var s: string | echo [s, s->empty(), s == null] # ['', 1, true] - var z: blob | echo [z, z->empty(), z == null] # [0z, 1, false] - var l: list | echo [l, l->empty(), l == null] # [[], 1, false] - var t: tuple | echo [t, t->empty(), t == null] # [(), 1, false] - var d: dict | echo [d, d->empty(), d == null] # [{}, 1, false] -< -Specialized types default to equaling `null`: >vim9 - - vim9script - var F: func | echo [F, F == null] # [function(''), true] - var j: job | echo [j, j == null] # ['no process', true] - var c: channel | echo [c, c == null] # ['channel fail', true] - class Class - endclass - var o: Class | echo [o, o == null] # [object of [unknown], true] - enum Enum - endenum - var e: Enum | echo [e, e == null] # [object of [unknown], true] -< - Note: See |empty()| for explanations of empty job, empty channel, and - empty object types. +not equal `null`: +>vim9 + vim9script + var n: number | echo [n, n->empty(), n == null] # [0, 1, false] + var f: float | echo [f, f->empty(), f == null] # [0.0, 1, false] + var b: bool | echo [b, b->empty(), b == null] # [false, 1, false] +< +Containers default to an empty container. Only an empty string equals `null`: +>vim9 + vim9script + var s: string | echo [s, s->empty(), s == null] # ['', 1, true] + var z: blob | echo [z, z->empty(), z == null] # [0z, 1, false] + var l: list | echo [l, l->empty(), l == null] # [[], 1, false] + var t: tuple | echo [t, t->empty(), t == null] # [(), 1, false] + var d: dict | echo [d, d->empty(), d == null] # [{}, 1, false] +< +Specialized types default to equaling `null`: +>vim9 + vim9script + var F: func | echo [F, F == null] # [function(''), true] + var j: job | echo [j, j == null] # ['no process', true] + var c: channel | echo [c, c == null] # ['channel fail', true] + class Class + endclass + var o: Class | echo [o, o == null] # [object of [unknown], true] + enum Enum + endenum + var e: Enum | echo [e, e == null] # [object of [unknown], true] +< + Notes: (1) See |empty()| for explanations of empty job, empty channel, + and empty object types. (2) The `class`, `enum`, and `typealias` + types cannot be used as values, so cannot be compared to `null` (see + |E1405|, |E1421|, and |E1403| respectively). Vim does not have a familiar null value. Instead, it has various null_ predefined values including |null_string|, |null_list|, and |null_job|. Primitives do not have a null_. Typical use cases for null_ are: - - to clear a variable and release its resources, - - as a default for a parameter in a function definition (for an example, - see |null_blob|), or - - assigned to a container or specialized variable to set it to null - for later comparison (for an example, see |null-compare|). +- to clear a variable and release its resources, +- as a default for a parameter in a function definition (for an example, + see |null_blob|), or +- assigned to a container or specialized variable to set it to null + for later comparison (for an example, see |null-compare|). For a specialized variable, like `job`, null_ is used to clear the -resources. For example: >vim9 - +resources. For example: +>vim9 vim9script var mydate: list def Date(channel: channel, msg: string): void - mydate->add(msg) + mydate->add(msg) enddef var myjob = job_start([&shell, &shellcmdflag, 'date'], {out_cb: Date}) echo [myjob, myjob->job_status()] @@ -2620,30 +2800,31 @@ resources. For example: >vim9 echo myjob < For a container variable, resources may also be cleared by assigning an -empty container to the variable. For example: >vim9 - +empty container to the variable. For example: +>vim9 vim9script var perfect: list = [1, 4] perfect->extend([9, 16, 25]) perfect = [] echo perfect - +< Using an empty container, rather than null_, to clear a container variable may avoid null complications - see |null-anomalies|. The initialization semantics of container variables and specialized variables differ. For containers: - - An uninitialized container defaults to empty but does not equal `null` - (except for a uninitialized string). - - A container initialized to [], (), {}, "", or 0z is empty but does not - equal `null`. - - A container initialized as null_ defaults to empty and equals `null`. +- An uninitialized container defaults to empty but does not equal `null` + (except for an uninitialized string). +- A container initialized to [], (), {}, "", or 0z is empty but does not equal + `null`. +- A container initialized as null_ defaults to empty and it also equals + `null`. In the following example, the uninitialized list ("lu") and [] initialized list ("li") are equivalent and indistinguishable whereas "ln" is a null container, which is similar to, but not equivalent to, an empty container -(see |null-anomalies|). >vim9 - +(see |null-anomalies|). +>vim9 vim9script # uninitialized: empty container, not null var lu: list @@ -2656,8 +2837,8 @@ container, which is similar to, but not equivalent to, an empty container echo ['ln', $"empty={ln->empty()}", $"null={ln == null}"] < Specialized variables default to equaling null. These job initializations -are equivalent and indistinguishable: >vim9 - +are equivalent and indistinguishable: +>vim9 vim9script var j1: job var j2: job = null_job @@ -2665,8 +2846,8 @@ are equivalent and indistinguishable: >vim9 echo (j1 == j2) == (j2 == j3) # true (equivalent, indistinguishable) < When a list, tuple, or dict is declared, if the item type is not specified -it cannot be inferred. Consequently, the item type defaults to "any": >vim9 - +it cannot be inferred. Consequently, the item type defaults to "any": +>vim9 vim9script var [t1, t2] = [(), null_tuple] echo $'t1 is {t1->typename()} and t2 is {t2->typename()} too' @@ -2674,40 +2855,38 @@ it cannot be inferred. Consequently, the item type defaults to "any": >vim9 Tuples and functions (or partials) may be declared in various ways. See |tuple-type|, |variadic-tuple|, and |vim9-func-declaration|. - *null-compare* + *null-compare* For familiar null compare semantics, where an empty container is not equal to a null container, do not use null_ in a comparison. That is because, in Vim9 script, although null_ == `null`, comparing an: +- empty container to `null` is `false`, but +- empty container to null_ is `true`. - - empty container to `null` is `false`, but - - empty container to null_ is `true`. - -So, compare against `null`, not null_. For example: >vim9 - +So, compare against `null`, not null_. For example: +>vim9 vim9script var bonds: dict> = {g: ['007', '008'], o: ['007', '009']} def Search(query: string): list - return query == "\r" ? null_list : bonds->get(query, []) + return query == "\r" ? null_list : bonds->get(query, []) enddef echo "Goldfinger (g) or Octopussy (o)?: " const C: string = getcharstr() var result: list = C->Search() if result == null # <<< DO NOT USE null_list HERE! - echo "Error: Nothing was entered" + echo "Error: Nothing was entered" else - echo result->empty() ? $"No matches for '{C}'" : $"{result}" + echo result->empty() ? $"No matches for '{C}'" : $"{result}" endif < - NOTE: Using "result == null_list" instead of "result == null" would - fail to distinguish the error (nothing entered) and the valid - (nothing matched) result because [] == null_list whereas - [] != null. + Note: Using "result == null_list" instead of "result == null" would + fail to distinguish the error (nothing entered) and the valid + (nothing matched) result because [] == null_list whereas [] != null. Conceptually, think of the null_ construct as a hybrid/bridge between the general `null` and typed `empty` containers, having properties of both. In the following section there are details about comparison results. - *null-details* *null-anomalies* + *null-details* *null-anomalies* This section describes issues about using null and null_; included below are the enumerated results of null comparisons. In some cases, if familiar with vim9 null semantics, the programmer may choose to use null_ in @@ -2715,8 +2894,8 @@ comparisons and/or other situations. Elsewhere in the documentation it says, "often a null value is handled the same as an empty value, but not always". For example, you cannot add to a -null container: >vim9 - +null container: +>vim9 vim9script var le: list = [] le->add('Okay') # le is now ['Okay'] @@ -2724,103 +2903,103 @@ null container: >vim9 ln->add("E1130") # E1130: Cannot add to null list < As explained in |null-compare|, there is a non-transitive relationship among -`null`, null_ containers, and `empty`. To recap, for example: >vim9 - +`null`, null_ containers, and `empty`. To recap, for example: +>vim9 vim9cmd echo (null_dict == {}, null_dict == null, {} != null) < The exception is an uninitialized string. It is equal to `null` (and is the -same instance as `null_string`). The 'is' operator (|expr-is|) may be used to -determine whether a string is uninitialized: >vim9 - +same instance as `null_string`). The "is" operator (|expr-is|) may be used to +determine whether a string is uninitialized: +>vim9 vim9script var s: string - echo s == null_string # true - echo s is null_string # true (the same instance) - echo s == null # true (unexpected, perhaps) - echo s is null # false (not the same instance) + echo s == null_string # true + echo s is null_string # true (the same instance) + echo s == null # true (unexpected, perhaps) + echo s is null # false (not the same instance) < However, don't do the same for the other containers because, when evaluated -against their applicable null_ with 'is', they return `false`: >vim9 - +against their applicable null_ with "is", they return `false`: +>vim9 vim9script var d: dict - echo d == null_dict # true - echo d is null_dict # false (not the same instance) - echo d == null # false (as expected) - echo d is null # false (not the same instance) + echo d == null_dict # true + echo d is null_dict # false (not the same instance) + echo d == null # false (as expected) + echo d is null # false (not the same instance) < The key distinction here is an uninitialized string is implemented as `null_string`, while an uninitialized list, dict, tuple, or blob is implemented as an empty container ([], {}, (), and 0z respectively). So, those uninitialized types are equal to, but not the same instance as, -their null_ counterparts, as this example shows: >vim9 - +their null_ counterparts, as this example shows: +>vim9 vim9script var t: tuple - echo t == null_tuple # true - echo t is null_tuple # false - + echo t == null_tuple # true + echo t is null_tuple # false +< However, a variable initialized to the null_ is equal not only to the -null_, it is also equal to null. For example: >vim9 - +null_, it is also equal to null. For example: +>vim9 vim9script var t: tuple = null_tuple - echo t == null_tuple # true - echo t is null_tuple # true - echo t == null # true + echo t == null_tuple # true + echo t is null_tuple # true + echo t == null # true < An uninitialized container variable is not equal to null, except for an uninitialized string, which is explained in an example, above. So, these -all echo `true`: >vim9 - - vim9script - var b: blob | echo b != null - var d: dict | echo d != null - var l: list | echo l != null - var t: tuple | echo t != null - var s: string | echo s == null - -An uninitialized specialized variable is equal to null so these all echo `true`: >vim9 - - vim9script - var c: channel | echo c == null - var F: func | echo F == null - var j: job | echo j == null +all echo `true`: +>vim9 + vim9script + var b: blob | echo b != null + var d: dict | echo d != null + var l: list | echo l != null + var t: tuple | echo t != null + var s: string | echo s == null +< +An uninitialized specialized variable is equal to null. So, these all +echo `true`: +>vim9 + vim9script + var c: channel | echo c == null + var F: func | echo F == null + var j: job | echo j == null class Class endclass - var nc: Class | echo nc == null + var nc: Class | echo nc == null enum Enum endenum - var ne: Enum | echo ne == null + var ne: Enum | echo ne == null < - Note: the specialized variables, like job, default to null and - no specialized variable has a corresponding empty value. + Note: the specialized variables, like job, default to null and no + specialized variable has a corresponding empty value. A container variable initialized to empty equals null_, so these are all -`true`: >vim9 - +`true`: +>vim9 vim9script - var s: string = "" | echo s == null_string - var b: blob = 0z | echo b == null_blob - var l: list = [] | echo l == null_list - var t: tuple = () | echo t == null_tuple - var d: dict = {} | echo d == null_dict + var s: string = "" | echo s == null_string + var b: blob = 0z | echo b == null_blob + var l: list = [] | echo l == null_list + var t: tuple = () | echo t == null_tuple + var d: dict = {} | echo d == null_dict < However, a container variable initialized to empty does not equal null, so -these are all `true`: >vim9 - +these are all `true`: +>vim9 vim9script - var s: string = "" | echo s != null - var b: blob = 0z | echo b != null - var l: list = [] | echo l != null - var t: tuple = () | echo t != null - var d: dict = {} | echo d != null + var s: string = "" | echo s != null + var b: blob = 0z | echo b != null + var l: list = [] | echo l != null + var t: tuple = () | echo t != null + var d: dict = {} | echo d != null < ============================================================================== - *generic-functions* -5. Generic functions +5. Generic functions *generic-functions* A generic function allows using the same function with different type arguments, while retaining type checking for arguments and the return value. @@ -2832,24 +3011,24 @@ Declaration~ *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: > - +Multiple type parameters are separated by commas: +> def[!] {funcname}<{type} [, {types}]>([arguments])[: {return-type}] - {function body} + {function body} enddef < *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 - +into a list of tuples: +>vim9 vim9script def Zip(first: list, second: list): list> - const LEN: number = ([first->len(), second->len()])->min() - final result: list> = [] - for i in range(LEN) - result->add((first[i], second[i])) - endfor - return result + const LEN: number = ([first->len(), second->len()])->min() + final result: list> = [] + for i in range(LEN) + result->add((first[i], second[i])) + endfor + return result enddef var n: list = [61, 62, 63] var s: list = ['a', 'b', 'c'] @@ -2861,18 +3040,18 @@ into a list of tuples: >vim9 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 - +valid whereas "n" is not: +>vim9 vim9script def MyFail(): void enddef - # E1552: Type variable name must start with an uppercase letter: n... + # E1552: Type variable name must start with an uppercase letter: n>()… < - *E1558* *E1560* + *E1558* *E1560* 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 - +errors: +>vim9 vim9script My1558() # E1558: Unknown generic function: My1558 @@ -2883,9 +3062,9 @@ errors: >vim9 My1560() # E1560: Not a generic function: My1560 < - *E1561* -Type parameter names must not clash with other identifiers: >vim9 - + *E1561* +Type parameter names must not clash with other identifiers: +>vim9 vim9script def My1561(): D enddef @@ -2897,46 +3076,42 @@ Type parameter names must not clash with other identifiers: >vim9 endenum def My1041(): E enddef - # E0141: Redefining script item "E" + # E1041: Redefining script item: "E" < Calling a generic function~ - *generic-function-call* + *generic-function-call* To call a generic function, specify the concrete types in "<" and ">" -between the function name and the argument list: > - +between the function name and the argument list: +> MyFunc>() < - NOTE: There are several working examples in this section, which may - be sourced, including |generic-function-example|. + Note: There are several working examples in this section, which may be + sourced, including |generic-function-example|. - *E1555* *E1556* *E1557* *E1559* + *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 - +Examples: +>vim9 vim9script def My1555<>(): void - enddef - # E1555: Empty type list specified for generic function ... + enddef # E1555: Empty type list specified for generic function 'vim9 vim9script def My1556(): void enddef - My1556() - # E1556: Too many types specified for generic function ... + My1556() # E1556: Too many types specified for generic f… < >vim9 vim9script def My1557(): void enddef - My1557() - # E1557: Not enough types specified for generic function ... + My1557() # E1557: Not enough types specified for generic funct… < >vim9 vim9script def My1559(): T enddef - My1559() - # Vim(eval):E1559: Type arguments missing for generic function ... + My1559() # E1559: Type arguments missing for generic function '… < Any Vim9 type (|vim9-types|) can be used as a concrete type in a generic function. @@ -2951,47 +3126,48 @@ 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 +An example: +>vim9 vim9script def Outer(): void # Returns either the first item of a list or a default value def FirstOrDefault(lst: list, default: U): any return lst->len() > 0 ? lst[0] : default enddef - echo FirstOrDefault(['B', 'C'], false) # echos B - echo FirstOrDefault([], 42) # echos 42 + echo FirstOrDefault(['B', 'C'], false) # B + echo FirstOrDefault([], 42) # 42 enddef Outer() < Using a type variable as a type argument ~ -A type variable may also be passed as a type argument. For example: >vim9 - +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(value: T): T - return value + return value enddef # U is declared as a type parameter # It is used for the 'value' parameter and the return type def CallId(value: U): U - # U is a type variable passed/used as a type argument - return Id(value) + # U is a type variable passed/used as a type argument + return Id(value) enddef echo CallId('I am') .. ' ' .. CallId(42) - +< This is useful for complex data structures like dictionaries of lists or, -as in the following example, lists of dictionaries: >vim9 - +as in the following example, lists of dictionaries: +>vim9 vim9script def Flatten(x: list>): list - final result: list = [] - for inner in x - result->extend(inner) - endfor - return result + final result: list = [] + for inner in x + result->extend(inner) + endfor + return result enddef const ENGLISH: list> = [{1: 'one'}, {2: 'two'}] const MANDARIN: list> = [{1: '壹'}, {2: '贰'}] @@ -3005,20 +3181,20 @@ the function, "T" is a type variable referencing that type parameter. Generic class method~ -A Vim9 class method can be a generic function: >vim9 - +A Vim9 class method can be a generic function: +>vim9 vim9script class Config - var settings: dict - def Get(key: string): T - return this.settings[key] - enddef + var settings: dict + def Get(key: string): T + return this.settings[key] + enddef endclass var c: Config = Config.new({timeout: 30, debug: true}) echo c.Get('timeout') echo c.Get('debug') < - *E1432* *E1433* *E1434* + *E1432* *E1433* *E1434* A generic class method in a base class can be overridden by a generic method 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, @@ -3028,34 +3204,34 @@ 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: >vim9 - +creating factories of functions that operate on specific types: +>vim9 vim9script # 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 = (c, c->char2nr()->string()) def Matcher(pattern: string): func(T): bool - return (value: T): bool => match(value, pattern) >= 0 + return (value: T): bool => match(value, pattern) >= 0 enddef var StringMatch = Matcher(char_dec[0]) - echo "*+"->StringMatch() # true (has *) - echo ",-"->StringMatch() # false + echo "*+"->StringMatch() # true (has *) + echo ",-"->StringMatch() # false var ListMatch = Matcher>(char_dec[1]) - echo [42, 43]->ListMatch() # true (has 42) - echo [44, 45]->ListMatch() # false + 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 -specific list of concrete types: > - +specific list of concrete types: +> defcompile MyFunc, dict> < The |:disassemble| command can be used to list the instructions generated for -a generic function: > - +a generic function: +> disassemble MyFunc> disassemble MyFunc> < @@ -3063,18 +3239,17 @@ a generic function: > Limitations and Future Work~ Currently, Vim does not support: - - Type inference for type variables: All types must be explicitly specified - when calling a generic function. - - Type constraints: It's not possible to restrict a type variable to a - specific class or interface (e.g., `T extends SomeInterface`). - - Default type arguments: Providing a default type for a type parameter - when not explicitly specified. +- Type inference for type variables: All types must be explicitly specified + when calling a generic function. +- Type constraints: It's not possible to restrict a type variable to a + specific class or interface (e.g., `T extends SomeInterface`). +- Default type arguments: Providing a default type for a type parameter + when not explicitly specified. ============================================================================== -6. Namespace, Import and Export - *vim9script* *vim9-export* *vim9-import* - +6. Namespace, Import and Export *vim9script* + *vim9-export* *vim9-import* A Vim9 script can be written to be imported. This means that some items are intentionally exported, made available to other scripts. When the exporting script is imported in another script, these exported items can then be used in @@ -3093,26 +3268,38 @@ only for things that really are global. Namespace ~ *vim9-namespace* -To recognize a file that can be imported the `vim9script` statement must -appear as the first statement in the file (see |vim9-mix| for an exception). -It tells Vim to interpret the script in its own namespace, instead of the -global namespace. If a file starts with: > +To recognize a file that can be imported the `vim9script` command must appear +as the first line in the file, however, see |vim9-mix| for an exception. It +tells Vim to interpret the script in its own namespace, instead of the global +namespace. If a file starts with: > +>vim9 vim9script var myvar = 'yes' -Then "myvar" will only exist in this file. While without `vim9script` it would -be available as `g:myvar` from any other script and function. +< +Then "myvar" will only exist in this file. While without `vim9script` it +would be available as `g:myvar` from any other script and function. *E1101* The variables at the file level are very much like the script-local "s:" variables in legacy Vim script, but the "s:" is omitted. And they cannot be deleted. - -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, have no specific type and they can be deleted. *E1304* - + *E1304* +In Vim9 script the global (`g:`), buffer (`b:`), window (`w:`), and tab (`t:`) +namespaces can be used like in legacy Vim script. In Vim9 script, namespace +prefixed variables: +- are not declared +- have no specific type, and +- can be deleted. +If a namespace prefixed variable is declared, |E1016| is given. And, if a +type is specified, E1304 is given: +>vim9 + vim9cmd var b:fails: bool # E1016: Cannot declare a buffer vari… + vim9cmd b:fails: bool = false # E1304: Cannot use type with this va… +< A side effect of `:vim9script` is that the 'cpoptions' option is set to the -Vim default value, like with: > +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, while flags added or removed in the script are also added to or removed from the @@ -3120,29 +3307,29 @@ original value to get the same effect. The order of flags may change. 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 here +There is one way to use both legacy and Vim9 syntax in one script file: +>vim9 + " _legacy Vim script_ comments are placed here if !has('vim9script') - " _legacy Vim script_ comments/commands here - finish + " _legacy Vim script_ comments and commands are placed here + finish endif vim9script # _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, and prevents the vim9script command from throwing an +syntax if possible, and prevents the vim9script command from giving 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. +syntax (such as "import from" instead of "import") might give errors. 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 - +patch 1232), a better condition is: +>vim9 if !has('patch-9.1.1232') echowindow $"Fail: Vim does not have patch 9.1.1232" finish @@ -3151,78 +3338,127 @@ patch 1232), a better condition is: >vim9 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 - executed and `finish` bails out before reaching `vim9script`. + 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 + executed and `finish` bails out before reaching `vim9script`. Export ~ - *:export* *:exp* + *:export* Exporting an item can be written as: > - export const EXPORTED_CONST = 1234 - export var someValue = ... - export final someValue = ... - export const someValue = ... - export def MyFunc() ... + export var myvar ... + export const MYCONST ... + export final myvar ... + export def MyDef() ... + export function MyFunc() ... export class MyClass ... - export interface MyClass ... + export interface MyInterface ... export enum MyEnum ... -< *E1043* *E1044* -As this suggests, only constants, variables, `:def` functions, classes, -interfaces and enums can be exported. - +< *E1043* +As this suggests, constants, variables, functions, classes, interfaces, +and enums can be exported. Trying to export something else gives E1043: +>vim9 + vim9cmd export echo 0 # E1043: Invalid command after :export +< *E1042* -`:export` can only be used in Vim9 script, at the script level. - +`:export` can only be used in a Vim9 script scope. So, for example, although +exporting a `:function` is okay, trying to export an item within such a +function gives E1042: +>vim9 + vim9script + export function F1042() + export var b: bool + endfunction + F1042() # E1042: Export can only be used in vim9script +< + *E1044* +An invalid argument can give E1044: +>vim9 + vim9script + export def /a1044(): void + enddef # E1044: Export with invalid argument: … +< Import ~ - *:import* *:imp* *E1094* *E1047* *E1262* - *E1048* *E1049* *E1053* *E1071* *E1088* *E1236* + *:import* The exported items can be imported in another script. The import syntax has -two forms. The simple form: > +two forms. The simpler form is: > import {filename} < Where {filename} is an expression that must evaluate to a string. In this form the filename should end in ".vim" and the portion before ".vim" will -become the script local name of the namespace. For example: > +become the script-local name of the namespace. For example: > import "myscript.vim" < This makes each exported item in "myscript.vim" available as "myscript.item". - *:import-as* *E1257* *E1261* -In case the name is long or ambiguous, this form can be used to specify -another name: > + + *E1094* +Importing is only allowed in the script-local scope. Trying to do so +elsewhere gives E1094; for example, from a `:function` or `:def` scope: +>vim9 + vim9script + def F1094(): void + import 'nah.vim' + enddef + F1094() # E1094: Import can only be used in a script +< + *E1053* *E1071* +Errors are given when either the {filename} is not found or it is invalid: +>vim9 + vim9cmd import 'e1053.vim' # E1053: Could not import "e1053.vim" + vim9cmd import false # E1071: Invalid string for :import: false +< + *:import-as* +When the name of the file is long, ambiguous (such as with filenames that are +the same but in different directories), or if you just want to use a distinct +structure for the name, this form can be used: > import {longfilename} as {name} < -In this form {name} becomes a specific script local name for the imported -namespace. Therefore {name} must consist of letters, digits and '_', like -|internal-variables|. The {longfilename} expression must evaluate to any -filename. For example: > - import "thatscript.vim.v2" as that -< *E1060* *E1258* *E1259* *E1260* -Then you can use "that.item", etc. You are free to choose the name "that". -Use something that will be recognized as referring to the imported script. -Avoid command names, command modifiers and builtin function names, because the -name will shadow them. It's better not to start the name with a capital -letter, since it can then also shadow global user commands and functions. -Also, you cannot use the name for something else in the script, such as a -function or variable name. - -In case the dot in the name is undesired, a local reference can be made for a +In this longer form, {name} becomes a specific script-local name for the +imported namespace. Therefore {name} must consist of letters, digits, or '_', +and cannot start with a digit, like |internal-variables|. The {longfilename} +expression must evaluate to an existing filename. + +The following script demonstrates using `:import-as`. It: +- Creates a constant, "TMP", for the {longfilename} of a temporary Vim9 script + file to import +- Writes a two-line Vim9 script to TMP +- Imports the Vim9 script file "as" the {name}, "that", and +- Echoes the result, showing the imported Vim9 script file's {longfilename} + and, on a separate line, the content of "that.myvar" (where "myvar" is an + exported variable in the imported script). +>vim9 + vim9script + const TMP: string = tempname() + ['vim9script', 'export var myvar: string = "YES!"']->writefile(TMP) + import TMP as that + echo $"Exported var 'myvar' in 'that' ({TMP}):\n" .. that.myvar +< +This example shows you can use "that.myvar", etc. You are free to choose the +name "that", or something that will be recognized as referring to the imported +script. It is best to avoid command names, command modifiers, and builtin +function names, because {name} will shadow any of those. It also is better to +begin the name with a lowercase letter because that avoids potential shadowing +of global user commands and functions. The {name} cannot be used for anything +else in the script, such as a function or variable name (see |E1213|), and it +cannot be assigned something else (see |E1258|). + +In case the "." in the name is undesired, a local reference can be made for a function: > - var LongFunc = that.LongFuncName + var LongDef = that.LongDefName This also works for constants: > - const MAXLEN = that.MAX_LEN_OF_NAME + const MAXLEN = that.MAXIMUM_LENGTH_OF_NAME This does not work for variables, since the value would be copied once and when changing the variable the copy will change, not the original variable. -You will need to use the full name, with the dot. +You need to use the full name (i.e., including the '.'). -`:import` can not be used in a function. Imported items are intended to exist -at the script level and only imported once. +Imported items are intended to exist at the script level and only be imported +once. Also, `:import` can not be used in a `:function` or `:def` - see |E1094|. -The script name after `import` can be: +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. @@ -3233,51 +3469,168 @@ The script name after `import` can be: longer and unique, to avoid loading the wrong file. Note that "after/import" is not used. -If the name does not end in ".vim" then the use of "as name" is required. - -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. +Once a Vim9 script file has been imported, the result is cached and it will +not be read again. -It is not allowed to import the same script twice, also when using two -different "as" names. +Many syntax and other errors may be given when importing. Examples: -When using the imported name the dot and the item name must be in the same -line, there can be no line break: > - echo that. - name # Error! - echo that - .name # Error! -< *import-map* + *E1047* +- Using an invalid "as" {name}: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i-cc + # E1047: Syntax error in import: i-cc +< + *E1048* +- Trying to use a non-existent item in an imported script: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc + echo i_cc.absent_item # E1048: Item not found in script: absent_item +< + *E1049* +- Trying to use a declared, but non-exported, item in an imported script: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc + echo i_cc.prepended # E1049: Item not exported in script: prepended +< + *E1060* +When using the imported name, the '.' and the item name must be contiguous. +So there can be neither white space nor a line break: +- After the '.' (see |E1074|), or +- Before the '.', for example: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc + echo i_cc .GetPath() # E1060: Expected dot after name: i_cc .GetP… +< + *E1088* +- Attempting to have an imported script import itself: +>vim9 + vim9script + const TMP: string = $'{tempname()}.vim'->substitute('\\', '/', 'g') + var lines: list = ['vim9script', $'import "{TMP}" as e1088'] + lines->writefile(TMP) + import TMP # E1088: Script cannot import itself +< + *E1236* +- Trying to use the script itself: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as cc + cc() # E1236: Cannot use cc itself, it is imported +< + *E1257* +- Not using "as {name}" when the imported filename does not end in ".vim": +>vim9 + vim9script + const TMP: string = tempname() + ['vim9script']->writefile(TMP) + import TMP # E1257: Imported script must use "as" or end in .vim: … +< + *E1258* +- Omitting the '.' after an imported name: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc + def F1258(): void + i_cc = + enddef + defcompile # E1258: No '.' after imported name: i_cc = +< + *E1259* +- Omitting the name after an imported name: +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc + def F1259(): void + i_cc.8 = 0 + enddef + defcompile # E1259: Missing name after imported name: i_cc.8 = 0 +< + *E1260* +- Attempting to `:unlet` an imported item: +>vim9 + vim9script + const TMP: string = tempname() + ['vim9script', 'export var b: bool']->writefile(TMP) + import TMP as e_unlet + unlet e_unlet.b # E1260: Cannot unlet an imported item: b +< + *E1261* +- Attempting to import the filename ".vim" without using "as". This script + writes the temporary file '.vim' to the operating system's applicable + temporary directory, then attempts to import it: +>vim9 + vim9script + const TMPDIR: string = tempname() + ->substitute('\\', '/', 'g') # Ensure Windows \ chars are / + ->substitute('/[^/]\+$', '/', '') # Remove the filename + ['vim9script']->writefile($'{TMPDIR}.vim') + echo $"Temporary import file name is: {TMPDIR}.vim" + import $'{TMPDIR}.vim' # E1261: Cannot import .vim without using "as" +< + *E1262* +- Trying to import the same script multiple times (including using different + "as" names): +>vim9 + vim9script + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc1 + import $'{$VIMRUNTIME}/autoload/ccomplete.vim' as i_cc2 + # E1262: Cannot import the same script twice: .../ccomplete.vim +< + *import-map* When you've imported a function from one script into a Vim9 script you can refer to the imported function in a mapping by prefixing it with ||: > noremap ,a :call name.Function() When the mapping is defined "name." will be replaced with and the script ID of the imported script. + An even simpler solution is using ||: > noremap ,a name.Function() Note that this does not work for variables, only for functions. - - *import-legacy* *legacy-import* -`:import` can also be used in legacy Vim script. The imported namespace still -becomes script-local, even when the "s:" prefix is not given. For example: > +This self-contained example demonstrates using , creating and +writing a temporary script, which is then imported and the function +"exported.MyDef()" mapped to a: +>vim9 + vim9script + const TMP: string = tempname() + ['vim9script', + 'export def MyDef(): void', + ' popup_notification("exported.MyDef() was called", {time: 4000})', + 'enddef']->writefile(TMP) + import TMP as exported + nnoremap a exported.MyDef() + # Now, pressing a calls exported.MyDef(), generating a popup +< + *import-legacy* *legacy-import* *:imp* +`:import`, including the shortened form `:imp`, can also be used in legacy Vim +script. The imported namespace still becomes script-local, even when the +"s:" prefix is not given. For example: > import "myfile.vim" call s:myfile.MyFunc() -And using the "as name" form: > - import "otherfile.vim9script" as that - call s:that.OtherFunc() - -However, the namespace cannot be resolved on its own: > - import "that.vim" - echo s:that - " ERROR: E1060: Expected dot after name: s:that +When using the "as name" form, the namespace cannot be resolved on its own +(see also |E1060|). This example demonstrates using `:imp` with `as` successfully, +then the error: > +>vim + let s:tmp = $'{tempname()}.vim'->substitute('\\', '/', 'g') + call writefile(['vim9script', + \ 'export function L()', + \ ' return "Vim9 script exporting a :function"', + \ 'endfunction'], s:tmp) + imp s:tmp as iLegacy + echo s:iLegacy.L() |" echoes 'Vim9 script exporting a :function' + echo s:iLegacy |" E1060: Expected dot after name: s:iLegacy < This also affects the use of || in the legacy mapping context. Since || is only a valid prefix for a function and NOT for a namespace, you -cannot use it to scope a function in a script local namespace. Instead of -prefixing the function with || you should use ||. For example: +cannot use it to scope a function in a script-local namespace. Instead of +prefixing the function with || you should use ||. +For example: > noremap ,a :call s:that.OtherFunc() < @@ -3291,45 +3644,45 @@ defined. This does not apply to autoload imports, see the next section. Importing an autoload script ~ - *vim9-autoload* *import-autoload* + *vim9-autoload* *import-autoload* 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 - referring to items imported from an autoload script. > - + referring to items imported from an autoload script. +> import autoload 'for/search.vim' command -nargs=1 SearchForStuff search.Stuff() +< + 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, either a relative or absolute - name can be used - see below. - - 2. In the autoload script put the bulk of the code. > + 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): void ... +< + This goes in .../autoload/for/search.vim. -< 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, 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). + 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. + 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. 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 @@ -3379,7 +3732,9 @@ members. See |Dictionary-function|. 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|. +of functionality, it is located in a separate help file, |vim9class.txt|, +though there are several sourceable examples within this help file too (at +|vim9-class-type|, |vim9-enum-type|, and elsewhere). ==============================================================================