Commit: runtime(doc): Update vim9.txt revisions, standardizing SS1,3-7
Christian Brabandt <[email protected]>
| Newsgroups | gmane.editors.vim.devel |
|---|---|
| Message-ID | <[email protected]> |
runtime(doc): Update vim9.txt revisions, standardizing SS1,3-7 Commit: https://github.com/vim/vim/commit/ba43008a2d6788eeb8ff78ae39dedd28598ae658 Author: Peter Kenny <[email protected]> Date: Fri Aug 21 14:45:31 2026 +0000 runtime(doc): Update vim9.txt revisions, standardizing SS1,3-7 closes: https://github.com/vim/vim/issues/20706 Signed-off-by: kennypete <[email protected]> Signed-off-by: Christian Brabandt <[email protected]> diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt index 310da6fab..1829c8276 100644 --- a/runtime/doc/index.txt +++ b/runtime/doc/index.txt @@ -1354,7 +1354,7 @@ tag command action ~ |:ex| :ex same as ":edit" |:execute| :exe[cute] execute result of expressions |:exit| :exi[t] same as ":xit" -|:export| :exp[ort] Vim9: export an item from a script +|:export| :export Vim9: export an item from a script |:exusage| :exu[sage] overview of Ex commands |:file| :f[ile] show or set the current file name |:files| :files list all files in the buffer list @@ -1398,7 +1398,7 @@ tag command action ~ |:imap| :im[ap] like ":map" but for Insert mode |:imapclear| :imapc[lear] like ":mapclear" but for Insert mode |:imenu| :ime[nu] add menu for Insert mode -|:import| :imp[ort] Vim9: import an item from another script +|:import| :import Vim9: import an item from another script |:inoremap| :ino[remap] like ":noremap" but for Insert mode |:inoreabbrev| :inorea[bbrev] like ":noreabbrev" but for Insert mode |:inoremenu| :inoreme[nu] like ":noremenu" but for Insert mode diff --git a/runtime/doc/tags b/runtime/doc/tags index ca5828a1e..a7b4e5bef 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -2690,7 +2690,6 @@ $quote eval.txt /*$quote* :execute eval.txt /*:execute* :exi editing.txt /*:exi* :exit editing.txt /*:exit* -:exp vim9.txt /*:exp* :export vim9.txt /*:export* :exu helphelp.txt /*:exu* :exusage helphelp.txt /*:exusage* @@ -11712,6 +11711,7 @@ vim9-boolean vim9.txt /*vim9-boolean* vim9-class vim9class.txt /*vim9-class* vim9-class-type vim9.txt /*vim9-class-type* vim9-classes vim9.txt /*vim9-classes* +vim9-closure vim9.txt /*vim9-closure* vim9-const vim9.txt /*vim9-const* vim9-curly vim9.txt /*vim9-curly* vim9-debug repeat.txt /*vim9-debug* @@ -11743,6 +11743,7 @@ vim9-partial-declaration vim9.txt /*vim9-partial-declaration* vim9-rationale vim9.txt /*vim9-rationale* vim9-reload vim9.txt /*vim9-reload* vim9-s-namespace vim9.txt /*vim9-s-namespace* +vim9-s:var vim9.txt /*vim9-s:var* vim9-scopes vim9.txt /*vim9-scopes* vim9-string-index vim9.txt /*vim9-string-index* vim9-typealias-type vim9.txt /*vim9-typealias-type* diff --git a/runtime/doc/vim9.txt b/runtime/doc/vim9.txt index 505482684..563119647 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 Aug 18 +*vim9.txt* For Vim version 9.2. Last change: 2026 Aug 21 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}) + 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 script-local variables may be used by prefixing + 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. @@ -925,7 +928,6 @@ Notes: \ }) < - White space ~ *vim9-white-space* *E1004* *E1068* *E1069* *E1074* *E1127* *E1202* Vim9 script enforces proper use of white space. This is no longer allowed: > @@ -1147,7 +1149,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 @@ -1335,15 +1337,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: <SNR>... + def F_1073() # E1073: Name already defined: <SNR... 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 @@ -1352,71 +1354,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 ar... 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 f... 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: ... enddef < The function will be compiled into instructions when @@ -1435,21 +1436,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 @@ -1467,10 +1468,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 @@ -1483,69 +1485,111 @@ 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 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 - + *vim9-s:var* +When referencing a script-local variable, the context determines whether using +the "s:" prefix is either mandatory, optional, or gives |E1268|. The context +factors are whether the script version is Vim9 script or legacy Vim script and +whether the reference is at the script-local level, within a `:def` function, +or within a `:function`. The three rules are: + + 1. In a `:function`, "s:" is always mandatory. This is regardless of the + script type or the function's parent context (such as nested within + another function). Similarly, "s:" is also mandatory in the script-local + scope of a legacy Vim script. + 2. In a Vim9 script, "s:" always gives |E1268| when used in a `:def` function, + regardless of the function's parent context. Similarly, it gives E1268 in + the script-local scope of a Vim9 script. + 3. In a legacy Vim script, "s:" is optional in a `:def` function, regardless + of the function's parent context. + +The following three scripts demonstrate these rules: +>vim + " 1. In a :function, "s:" is always mandatory. It is also mandatory + " in a legacy Vim script's script-local scope + let s:MyVar = v:true + echo s:MyVar | " v:true + " echo MyVar (Would give E121: Undefined variable: MyVar) + vim9cmd echo MyVar # true + function! MyFunc() + echo s:MyVar | " v:true + " echo MyVar (Would give E121: Undefined variable: MyVar) + vim9cmd echo MyVar # true + endfunction + call MyFunc() +< >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 + # 2. In a Vim9 script, "s:" gives E1268 when used in any :def function + # and in the script-local scope + var MyVar: bool = true + echo MyVar # true + # echo s:MyVar (Would give E1268: Cannot use s: in Vim9...) + legacy echo s:MyVar | # v:true + def MyFunc() + echo MyVar # true + # echo s:MyVar (Would give E1268: Cannot use s: in Vim9...) + legacy echo s:MyVar | # v:true enddef - var unus: number = 1 - MyVim9def() # MyVim9def is compiled ("duo" does not exist yet) - var duo: 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 - - " legacy Vim script - def! MyLegacyDef(): void - echo [unus, s:unus] # Echoes [1, 1] - # (If uncommented) First sourcing of 'echo s:duo' is E121 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 + MyFunc() +< >vim + " 3. In a legacy Vim script, "s:" is optional in a :def function + let s:MyVar = v:true + function! Outer() + def! MyFunc() + echo MyVar # true + echo s:MyVar # true + enddef + call MyFunc() + endfunction + call Outer() +< +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 MyDef() + if exists_compiled('MyVar') # evaluated at compile time + echo $"MyVar = {MyVar}" # MyVar = 1 + endif + if exists_compiled('MyVar2') # evaluated at compile time + echo $"MyVar2 = {MyVar2}" # not reached + else + echo "MyVar2 does not exist at compile time" + endif + if exists('MyVar') # evaluated at runtime + echo $"MyVar = {MyVar}" # MyVar = 1 + endif + if exists('MyVar2') # evaluated at runtime + # The following would give E1001: Variable not found: MyVar2 + # echo MyVar2 + else + echo "MyVar2 does not exist at runtime" + endif enddef - let s:unus = 1 - call MyLegacyDef() " Calls MyLegacyDef() and compiles if not already - let s:duo = 2 + var MyVar: number = 1 # Declared before MyDef() is compiled + MyDef() + var MyVar2: number = 2 # Declared after MyDef() is compiled +< < *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 @@ -1554,13 +1598,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.) @@ -1575,7 +1619,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. @@ -1584,22 +1628,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. @@ -1608,104 +1652,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 <SNR>. Depending on options, including |wildmenumode()|, completion may work with "s:", "<S", or the function - name directly. (For example, in Vim started with |-u| NONE, ":disa s:" - and |c_CTRL-E| lists script-local function names.) + name directly. (For example, in Vim started with |-u| NONE, + ":disa s:" and |c_CTRL-E| lists script-local function names.) Limitations ~ Variables local to `:def` functions are not visible to string evaluation. The following example shows that the script-local constant "SCRIPT_LOCAL" is -visible whereas the function-local constant "DEF_LOCAL" is not: >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<string> - const DEF_LOCAL: list<string> = ['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<string> = ['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<string> - const DEF_LOCAL: list<string> = ['A', 'def-local', 'list'] - return [1]->map((_, v) => DEF_LOCAL[v]) + const DEF_LOCAL: list<string> = ['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<func> 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<func> 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<func> 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, @@ -1716,42 +1761,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~ @@ -1771,7 +1816,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|). @@ -1779,38 +1825,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 ~ @@ -1819,86 +1865,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 valu... + 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<number - vim9cmd var l: list<invalidtype> - vim9cmd var l: list<number> = ['42'] +are examples of errors E1008, E1009, E1010, and E1012: +>vim9 + vim9cmd var l_1008: list + vim9cmd var l_1009: list<number + vim9cmd var l_1010: list<invalidtype> + vim9cmd var l_1012: list<number> = ['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<number> = [1, 2] var t: tuple<...list<number>> = (3, 4) @@ -1916,111 +1963,267 @@ tuple<...list<number>> a variadic tuple with zero or more items of tuple<number, ...list<string>> a tuple with an item of type |Number| followed by zero or more items of type |String| -Examples: > - var myTuple: tuple<number> = (20,) - var myTuple: tuple<number, string> = (30, 'vim') - var myTuple: tuple<number, float, bool> = (40, 1.1, true) - var myTuple: tuple<...list<string>> = ('a', 'b', 'c') - var myTuple: tuple<number, ...list<string>> = (3, 'a', 'b', 'c') +Examples: +>vim9 + vim9script + var t1: tuple<number> = (20,) + var t2: tuple<number, string> = (30, 'vim') + var t3: tuple<number, float, bool> = (40, 1.1, true) + var t4: tuple<...list<string>> = ('a', 'b', 'c') + var t5: tuple<number, ...list<string>> = (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<number>> = (1, 2, 3) - var myTuple: tuple<...list<string>> = ('a', 'b', 'c') - var myTuple: tuple<...list<bool>> = () -< - *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<number>> = (1, 2, 3) + var t7: tuple<...list<string>> = ('a', 'b', 'c') + var t8: tuple<...list<bool>> = () + var tE: tuple<...any> = () # E1539: Variadic tuple must end with li... +< + *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<number>): void + echo l->reduce((x, y) => x + y) + enddef + const F: func(...list<number>): 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>): number + return l->reduce((x, y) => x + y) + enddef + const F: func(...list<number>): 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<number>): void + echo (a + l->reduce((x, y) => x + y))->float2nr() + enddef + const F: func(float, ...list<number>): 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>): number + return (a + l->reduce((x, y) => x + y))->float2nr() + enddef + const F: func(float, ...list<number>): 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 $" The 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 < - *vim9-typealias-type* + *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* 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<number> 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<number> 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<string, float> + def InfoArea(): tuple<string, float> endinterface class Circle implements Shape - var radius: float - def InfoArea(): tuple<string, float> - return ('Circle (π × r²)', 3.141593 * this.radius->pow(2)) - enddef + var radius: float + def InfoArea(): tuple<string, float> + return ('Circle (π × r²)', 3.141593 * this.radius->pow(2)) + enddef endclass class Square implements Shape - var side: float - def InfoArea(): tuple<string, float> - return ('Square (s²)', this.side->pow(2)) - enddef + var side: float + def InfoArea(): tuple<string, float> + return ('Square (s²)', this.side->pow(2)) + enddef endclass const INPUT: float = "Enter a float value: "->input()->str2float() echo " Areas of shapes:" @@ -2028,8 +2231,8 @@ classes, objects, and interface: >vim9 var mySquare: object<Square> = Square.new(INPUT) final shapes: list<Shape> = [myCircle, mySquare] for shape in shapes - const [N: string, A: float] = shape.InfoArea() - echo $" - {N} has area of {A}" + const [N: string, A: float] = shape.InfoArea() + echo $" - {N} has area of {A}" endfor echo " type() typename() ------ ----------" echo $"Circle {Circle->type()} {Circle->typename()}" @@ -2043,19 +2246,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 $" A {this.name} has " .. - $"{this.eq} equal sides and {this.ra} right angles " - enddef + Square('four', 'only'), + Rhombus('opposite', 'no') + var eq: string + var ra: string + def string(): string + return $" A {this.name} has " .. + $"{this.eq} equal sides and {this.ra} right angles " + enddef endenum echo "Rhombus (r) or Square (s)?" var myQuad: Quad = getcharstr() =~ ' -- -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php --- You received this message because you are subscribed to the Google Groups "vim_dev" group. To unsubscribe from this group and stop receiving emails from it, send an email to [email protected]. To view this discussion visit https://groups.google.com/d/msgid/vim_dev/E1wxQxW-00FPm5-PZ%40256bit.org.