From f126f3bccb9d6ad96b087f1b217b1761286a34e9 Mon Sep 17 00:00:00 2001 From: h-east Date: Tue, 25 Nov 2025 22:26:57 +0900 Subject: [PATCH] update vim9.{txt,jax} just before '*variable-types*' --- doc/vim9.jax | 971 +++++++++++++++++++++++++++++++++++++-------------- en/vim9.txt | 888 ++++++++++++++++++++++++++++++++++------------ 2 files changed, 1375 insertions(+), 484 deletions(-) diff --git a/doc/vim9.jax b/doc/vim9.jax index fd4409f99..1ceac78ef 100644 --- a/doc/vim9.jax +++ b/doc/vim9.jax @@ -22,12 +22,13 @@ script の新しい文法と機能について書かれている。 ------------------------------------------------------------------------------ - NOTE: この vim9.txt ヘルプファイルでは、`vim9script` で始まる Vim9 script の - コードブロックは、Vim9 script の構文がハイライトされている。また、これ - らはソースとして実行できるため、実行して出力内容を確認できる。ソースと - して実行するには、`:'<,'>source` を使用する (|:source-range| を参照)。 - これは、|V| で行を視覚的に選択し、`:so` と入力することで実行できる。 - 例えば、以下の Vim9 script で試してみてほしい: >vim9 + NOTE: この vim9.txt ヘルプファイルでは、`vim9script` (および `vim9cmd` で始 + まる個々の行) で始まる Vim9 script のコードブロックは、Vim9 script の + 構文がハイライトされている。また、これらはソースとして実行できるため、 + 実行して出力内容を確認できる。ソースとして実行するには、`:'<,'>source` + を使用する (|:source-range| を参照)。これは、|V| で行を視覚的に選択し、 + `:so` と入力することで実行できる。例えば、以下の Vim9 script で試して + みてほしい: >vim9 vim9script echowindow "Welcome to Vim9 script!" @@ -63,7 +64,7 @@ Vim9 script の主な目的は劇的な性能の向上である。これは、 りのオーバーヘッドが必要になる。そのため、Vim9 script では、この辞書が使用でき なくなった。その他の違いは、エラーの処理方法など、より微細なものである。 -Vim9 script は以下の場所で使用することができる: +Vim9 script の構文、セマンティクス、および動作は以下に適用される: - コマンド `:def` で定義された関数の中 - コマンド `vim9script` で始まるスクリプトファイルの中 - 上記のコンテキストで定義された自動コマンド @@ -77,18 +78,76 @@ Vim9 script と旧来の Vim script を混在させることができる。古 直す必要はなく、以前と同じように動作する。高速化が必要なコードには、いくつかの `:def` 関数を使用するとよいだろう。 -:vim9[cmd] {cmd} *:vim9* *:vim9cmd* *E1164* - Vim9 script の構文とセマンティクスを使用して {cmd} を評価、実 - 行する。コマンドを入力する時や、旧来のスクリプトまたは関数内で - 便利である。 +:vim9[cmd] {cmd} *:vim9* *:vim9cmd* + Vim9 script の構文、セマンティクス、および動作を使用して {cmd} + を評価し、実行する。`:function` や旧来の Vim script 内でコマン + ドを入力するときに便利である。 + + 次の短い例は、旧来の Vim script コマンドと :vim9cmd (つまり + Vim9 script コンテキスト) が似ているように見えるものの、構文だ + けでなく意味や動作も異なる可能性があることを示している。 >vim + + call popup_notification('entrée'[5:] + \ ->str2list()->string(), #{time: 7000}) + vim9cmd popup_notification('entrée'[5 :] + ->str2list()->string(), {time: 7000}) +< + Notes: 1) 出力が異なる理由は、Vim9 script は文字インデックス + を使用するのに対し、旧来の Vim script はバイトイン + デックスを使用するためである。|vim9-string-index| + を参照。 + 2) 構文も異なる。Vim9 script では: + - "[5 :]" 内のスペースは必須である + (|vim9-white-space| を参照)。 + - "\" による行継続は必須ではない。 + - "#" (辞書キーを引用符で囲むのを避けるため) は必須 + ではなく、許可もされない - |#{}| を参照。 + + *E1164* + `:vim9cmd` は単独では実行できない。コマンドが後に続く必要があ + る。 + +:leg[acy] {cmd} *:leg* *:legacy* + {cmd} を旧来の Vim script の構文、意味、および動作を使用して + 評価および実行する。これは、Vim9 script または `:def` 関数での + み適用できる。上記のスクリプトと同等のスクリプトを使用する場合 + (出力が異なる理由については、スクリプトの注記を参照): >vim9 + + vim9script + # レガシーコンテキスト - つまり、これは [769, 101] のポップ + # アップを作成する + legacy call popup_notification('entrée'[5:] + \ ->str2list()->string(), #{time: 7000}) + # Vim9 script コンテキスト - これは [101] のポップアップを作 + # 成する + popup_notification('entrée'[5 :] + ->str2list()->string(), {time: 7000}) +< + Vim9 script のスクリプトローカル変数は、旧来の Vim script と同 + 様に、プリフィックスに "s:" を付けることで使用できる。この例 + は、構文の違いを示している。Vim9 script ではスクリプトローカル + 変数は "k" だが、旧来の Vim script では "s:k" である。 >vim9 + + vim9script + var k: string = "Okay" + echo k + legacy echo s:k +< *E1189* + `:legacy` はコンパイルされた Vim9 script の制御フローコンテキ + ストでは使用できない。例: >vim9 + + vim9script + def F_1189() + if v:version == 900 + # E1189: Cannot use :legacy with this command: endif + legacy endif + enddef + F_1189() +< *E1234* + `:legacy` は単独では使用できず、その後にコマンドを続ける必要が + ある。 -:leg[acy] {cmd} *:leg* *:legacy* *E1189* *E1234* - 旧来のスクリプト構文とセマンティクスを使用して {cmd} を評価、 - 実行する。Vim9 script または :def 関数内でのみ便利である。 - Note {cmd} は旧来の式構文で解析されるため、ローカル変数を使用 - できないことに注意。 -|52.6| の Vim9 script の例を参照。 ============================================================================== 2. 旧来の Vim script からの変更点 *vim9-differences* @@ -104,6 +163,7 @@ Vim9 script と :def 関数を使用するときに最も頻繁に遭遇する .. yourName .. ", how are you?" - 読みやすさを向上させるために、多くの場所で空白が必要である。 + |vim9-white-space| を参照。 - `:let` *E1126* を使用せずに値を代入し、`:var` を使用して変数を宣言する: > var count = 0 count += 3 @@ -231,7 +291,7 @@ Vim9 関数 ~ var d = {func: Legacy, value: 'text'} d.func() enddef -< *E1096* *E1174* *E1175* + 引数の型と戻り値の型を指定する必要がある。"any" 型を使用することができ、その場 合は旧来の関数と同様に実行時に型チェックが行われる。 *E1106* @@ -272,7 +332,7 @@ Vim9 script のスクリプトレベルで `:function` または `:def` を使 def ThisFunction() # スクリプトローカル def g:ThatFunction() # グローバル export def Function() # import および import autoload 用 -< *E1058* *E1075* +< *E1075* `:def` 関数内で `:function` または `:def` を使用してネストされた関数を指定し、 名前空間が指定されていない場合、このネストされた関数は定義されているコードブ ロックに対してローカルになる。文字列引数を持つ `function()` で使用することはで @@ -829,7 +889,7 @@ Notes: 空白 ~ - *E1004* *E1068* *E1069* *E1074* *E1127* *E1202* + *vim9-white-space* *E1004* *E1068* *E1069* *E1074* *E1127* *E1202* Vim9 script では空白の適切な使用を強制する。これはもう許可されていない: > var name=234 # エラー! var name= 234 # エラー! @@ -1218,284 +1278,592 @@ Note 認識されていないコマンドを "|" でつなぐと、その後の 式中で ++var や --var を使うことはまだサポートされていません。 + ============================================================================== 3. 新しいスタイルの関数 *fast-functions* - *:def* *E1028* + *:def* :def[!] {name}([arguments])[: {return-type}] - {name} という名前の新しい関数を定義します。関数の本体 - は次の行から `:enddef` と一致するまで続きます。 - *E1073* - *E1011* - {name} は必ず 100 バイト未満でなければなりません。 - *E1003* *E1027* *E1056* *E1059* - `:return` で返される値の型は必ず {return-type} と一致 - しなければなりません。{return-type} が省略された、ある - いは "void" である場合は、その関数は何も返さないと想定 - されます。 - *E1077* *E1123* - {arguments} は 0 あるいはそれ以上の引数の宣言列です。 - 引数の宣言には 3 つの書式があります: + {name} という名前で新しい関数を定義する。関数本体は、 + 対応する `:enddef` まで、次の行に記述する。 + *E1073* + {name} はスクリプトローカルレベルでは再利用できない: >vim9 + + vim9script + def F_1073() + enddef + def F_1073() # E1073: Name already defined: ... + enddef +< *E1011* + {name} の長さは 100 バイト未満である必要がある。 + + *E1077* + {arguments} は、0 個以上の引数宣言の並びである。以下の + 3 つの形式がある: {name}: {type} {name} = {value} {name}: {type} = {value} - 最初の書式は必須の引数の書式で、呼び出す側は必ず実引数 - を与える必要があります。 - 2 つ目と 3 つ目の書式は任意の引数の書式です。呼び出す - 側が実引数を省略した場合は、{value} が使われます。 - - 関数は呼び出された時、`:disassemble` が使われたとき、 - あるいは `:defcompile` が使われたときに命令列にコンパ - イルされます。文法および型のエラーはこの時に提示されま - す。 - - `:def` を他の `:def` や `:function` の内側で大体 50 階 - 層の深さまでまでネストすることが可能です。 + 最初の形式は必須の引数である。そのため、宣言では型を指 + 定する必要がある。例: >vim9 + + vim9script + def F_1077(x): void + # E1077: Missing argument type for x + enddef +< + 2 番目の形式の場合、宣言で型が指定されていないため、 + Vim は型を推測する。2 番目と 3 番目の形式の両方におい + て、呼び出し側で {value} が省略された場合はデフォルト + の {value} が適用される。例: >vim9 + + vim9script + def SecondForm(arg = "Hi"): void + 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}' + enddef + ThirdForm() +< *E1123* + `:def` 関数で呼び出される組み込み関数の引数は、引数間 + にコンマが必要である: >vim9 + + vim9script + def F_1123(a: number, b: number): void + echo max(a b) + # E1123: Missing comma before argument: b) + enddef + F_1123(1, 2) +< *E1003* *E1027* *E1096* + `:return` で使用される値の型は {return-type} と一致す + る必要がある。{return-type} が省略されているか "void" + の場合、関数は何も返せない。例: >vim9 + + vim9script + def F_1003(): bool + return # E1003: Missing return value + enddef + F_1003() +< >vim9 + vim9script + def F_1027(): bool + echo false # E1027: Missing return statement + enddef + F_1027() +< >vim9 + vim9script + def F_1096(): void + return false # E1096: Returning a value ... + enddef + F_1096() +< *E1056* *E1059* + ": {return-type}" が指定されている場合、{return-type} + は省略できない (ぶら下がりコロンが残る)。また、": " の + 前に空白文字を置くこともできない。例: >vim + + def F_1056(): + # E1056: Expected a type: + enddef + def F_1059() : bool + # E1059: No white space allowed before colon:... + enddef +< + 関数は、呼び出されたとき、または `:defcompile` または + `:disassemble` が使用されたときに命令にコンパイルされ + る。(例については、|:disassemble| を参照。) その際に構 + 文エラーと型エラーが生成される。 + + *E1058* + `:def` を別の `:def` または `:function` 内にネストでき + るのは最大 49 レベルまでである。50 レベルを超えると + |E1058| エラーが発生する。 + *E1117* - [!] は `:function` と同様に使われます。Note Vim9 - script において、スクリプトローカル関数は後で削除され - たり再定義されたりしてはいけません。スクリプトローカル - の削除は、同じスクリプトの再読み込みによってのみ行えま - す。 + [!] は、関数の再定義 (`:function`! と同様) を許可する + ため、旧来の Vim script でのみ使用できる。Vim9 script + では、スクリプトローカルな関数は削除または再定義できな + いため、! は使用できない。ただし、スクリプトを再読み込 + みすることで削除できる。また、ネストされた関数では、再 + 定義に ! は使用できない。例: >vim + + " 旧来の Vim script の :def! の例 + def! LegacyFunc() + 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 + enddef + Func() +< >vim9 + vim9script + def! F_477(): void # E477: No ! allowed + enddef +< >vim9 + vim9script + def F_1084(): void + enddef + delfunction! F_1084 + # E1084: Cannot delete Vim9 script function F_1084 +< + Note: 一般的なエラー *E1028* ("Compiling :def function + failed") は、コンパイル中に発生した原因不明のエラーを + 示す。再現可能な場合は、Vim のエラー報告に欠陥がある可 + 能性があるため、https://github.com/vim/vim/issues に報 + 告してほしい。 *:enddef* *E1057* *E1152* *E1173* -:enddef `:def` で定義された関数の終了。`:enddef` はそれだけで - 行にあるべきです。 +:enddef `:def` で定義された関数の終了。単独の行に記述する必要 + がある。例: >vim9 -また、このwikiも参考になるでしょう。これは Vim9 script のアーリーアダプターに -よって書かれました: https://github.com/lacygoill/wiki/blob/master/vim/vim9.md + vim9script + def MyFunc() + echo 'Do Something' | enddef + # E1057: Missing :enddef +< >vim9 + vim9script + def F_1173() + enddef echo 'X' + # E1173: Text found after enddef: echo 'X' +< >vim9 + vim9script + def F_1152() + function X() + enddef # E1152: Mismatched enddef + enddef +< +こちらの Wiki も役に立つかもしれない。これは Vim9 sript のアーリーアダプターに +よって書かれたものである: +https://github.com/lacygoill/wiki/blob/master/vim/vim9.md -もし関数が定義されたスクリプトが Vim9 script であるなら、スクリプトローカル変 -数はプリフィックス "s:" なしでアクセスすることができます。それらは関数がコンパ -イルされる前に定義されている必要があります。もし関数が定義されたスクリプトが旧 -来の Vim script であるなら、スクリプトローカル変数は、コンパイル時に存在しない -場合は、プリフィックス "s:" をつけてアクセスする必要があります。 - *E1269* -|Vim9| script では、スクリプトローカル変数はスクリプトレベルで宣言されなければ -なりません。それらは関数内で、旧来の関数内でも作成することはできません。 +`:def` 関数が定義されているスクリプトが Vim9 script の場合、スクリプトローカル +変数は "s:" プリフックスを使用せずにアクセスする必要がある。これらの変数は関数 +がコンパイルされる前に定義する必要があり、エラーを回避する方法はない (例えば、 +|exists()| を使用して未宣言の変数を条件付きでスキップするなど)。例: >vim9 + vim9script + def MyVim9def() + echo unus # 1 を表示する + # echo s:unus # これは E1268 (Cannot use s: in Vim9) になる + if exists('duo') + # echo duo # これは E1001 (Variable not found: duo) になる + endif + enddef + var unus: number = 1 + MyVim9def() # MyVim9def がコンパイルされる ("duo" はまだ存在し + # ていない) + var duo: number = 2 +< +`:def` 関数が定義されているスクリプトが旧来の Vim script である場合、スクリプ +トローカル変数は "s:" プリフィックスの有無にかかわらずアクセスできる。ただし、 +"s:" を使用すると変数の解決が実行時に遅延されるため、まだ存在しない可能性のあ +る変数によるコンパイルエラーを回避できる。次の例で説明する: >vim + + " 旧来の Vim script + def! MyLegacyDef(): void + echo [unus, s:unus] # [1, 1] を表示する + # (コメントアウトされていない場合) 'echo s:duo' の最初のソースは + # E121 となり、コンパイルエラーが発生する。後続のソースは 2 を表示 + # する + # echo s:duo + if exists("s:duo") + # 最初のソース: 表示をスキップ; 後続のソース: 2 を表示 + echo s:duo + endif + if exists("duo") + # (コメントアウトされていない場合) 'echo duo' の最初のソースは + # E1001 で、コンパイルエラーが発生する。後続のソースは 2 を表 + # 示する + # echo duo + endif + enddef + let s:unus = 1 + call MyLegacyDef() " MyLegacyDef() を呼び出し、まだコンパイルされてい + " ない場合はコンパイルする + let s:duo = 2 +< *E1269* +Vim9 script 内のスクリプトローカル変数は、スクリプトレベルで宣言する必要があ +る。`:def` 関数内では作成できず、また "s:" プリフィックスを持つ旧来の関数内で +も宣言できない。例: >vim9 + + vim9script + function F_1269() + 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[ompile] 現在のスクリプトで定義されている関数とクラス - (|class-compile|) のうち、まだコンパイルされていないも - のをコンパイルします。これはコンパイル時に見つかったい - かなるエラーも報告します。 +:defc[ompile] 現在のスクリプトで定義されていてまだコンパイルされてい + ない関数とクラス (|class-compile|) をコンパイルする。 + これにより、コンパイル中に見つかったエラーが報告され + る。 + + 例: (`enddef` までの) 3 行がソースコードに読み込まれる + と、Vim9 の `:def` 関数がコンパイルされないためエラー + は発生しない。しかし、4 行すべてがソースコードに読み込 + まれると、コンパイルは失敗する: >vim9 -:defc[ompile] MyClass クラス内のすべてのメソッドをコンパイルします。 - |class-compile| + vim9script + def F_1027(): string + enddef + defcompile F_1027 # E1027: Missing return statement + +:defc[ompile] MyClass クラス内のすべてのメソッドをコンパイルする。(例につい + ては |:disassemble| を参照。) :defc[ompile] {func} :defc[ompile] debug {func} :defc[ompile] profile {func} - 必要であれば関数 {func} をコンパイルします。"debug" と - "profile" はコンパイルモードを指定するのに使います。こ - れはコンパイル時に見つかったいかなるエラーも報告しま - す。 - {func} コールは、"ClassName.functionName" とすること - で、クラス内の関数やメソッドもコンパイルできます。 - {func} コールは、"ClassName" とすることで、クラス内の - すべての関数とメソッドをコンパイルすることもできます。 + 必要に応じて関数 {func} をコンパイルする。コンパイル + モードを指定するには、"debug" と "profile" を使用する。 + これにより、コンパイル中に見つかったエラーが報告され + る。 + {func} を "ClassName.functionName" にすることで、クラ + ス内の関数またはメソッドをコンパイルすることもできる。 + {func} を "ClassName" にすることで、クラス内のすべての + 関数とメソッドをコンパイルすることもできる。 *:disa* *:disassemble* -:disa[ssemble] {func} {func} 用に生成された命令列を表示します。これはデバ - ッグ及びテスト用です。 *E1061* - Note {func} のコマンドライン補完において、スクリプト - ローカル関数を見つけるのに "s:" を前置することができ - ます。 +:disa[ssemble] {func} {func} に対して生成された命令を表示する。 + これはデバッグとテスト用である。 + {func} が見つからない場合、エラー *E1061* が発生する。 + {func} を "ClassName.functionName" にすることで、クラ + ス内の関数を逆アセンブルすることもできる。 + 次の例は、|class| で `:defcompile` を使用し、 + "ClassName.functionName" で `:disassemble` を使用する + 方法を示している (カーソルはビジュアルソースのスクリプ + トの最終行にある): >vim9 + + vim9script + class Line + 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() # カーソル位置はココにある-->_ :disa[ssemble] profile {func} - `:disassemble` と似ていますが、プロファイルをとるとき - に使われる命令列を表示します。 + `:disassemble` と似ているが、プロファイリングに使用さ + れる命令が含まれている。 :disa[ssemble] debug {func} - `:disassemble` と似ていますが、デバッグをするときに使 - われる命令列を表示します。 + `:disassemble` と似ているが、デバッグに使用される命令 + が含まれている。 -制約 ~ + Note: {func} のコマンドライン補完では、スクリプトローカル関数が ととも + に表示される。|wildmenumode()| などのオプションによっては、"s:"、" - def MapList(): list - var list = ['aa', 'bb', 'cc', 'dd'] - return range(1, 2)->map('list[v:val]') + +制限事項 ~ + +`:def` 関数のローカル変数は文字列評価では参照できない。 +以下の例は、スクリプトローカル定数 "SCRIPT_LOCAL" は参照できるが、関数ローカル +定数 "DEF_LOCAL" は参照できないことを示している: >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 enddef + echo 'script local'->MapList() # ['script-local'] を表示する + echo 'def local'->MapList() # E121: Undefined variable: DEF_LOCAL +< +map 引数は文字列式であり、関数スコープ外で評価される。Vim9 script では、代わり +にラムダ式を使用する: >vim9 -この map の引数は関数のスコープ抜きに評価される文字列式です。代わりにラムダ式 -を使ってください: > + vim9script def MapList(): list - var list = ['aa', 'bb', 'cc', 'dd'] - return range(1, 2)->map((_, v) => list[v]) + const DEF_LOCAL: list = ['A', 'def-local', 'list'] + return [1]->map((_, v) => DEF_LOCAL[v]) enddef + echo MapList() # ['def-local'] を表示する +< +`:edit` などのコンパイルされないコマンドの場合、|backtick-expansion| を使用で +き、ローカルスコープを使用できる。例: >vim9 -`:edit` のような、コンパイルされないコマンドには、バッククォートによる展開が -使え、またそれはローカルスコープを使うことができます。例: > - def Replace() - var fname = 'blah.txt' - edit `=fname` + vim9script + def EditNewBlah() + var fname: string = 'blah.txt' + split + edit `=fname` enddef + EditNewBlah() # 新しい分割がバッファ 'blah.txt' として作成される +< +ループ内で定義されたクロージャは、変数の宣言場所に応じて、変数を共有するか、そ +れぞれ独自のコピーを持つかを選択できる。ループ外で変数を宣言した場合、すべての +クロージャは同じ共有変数を参照する。 +以下の例は、"outloop" 変数が 1 つだけ存在する場合の結果を示している: >vim9 -ループ内で定義されたクロージャは同じコンテキストを共有します。例: > + vim9script var flist: list - for i in range(5) - var inloop = i - flist[i] = () => inloop - endfor - echo range(5)->map((i, _) => flist[i]()) - # 結果: [4, 4, 4, 4, 4] -< *E1271* -クロージャはそのコンテキスト内の変数を見つけられるように、必ずそれが定義された -コンテキストでコンパイルされます。これは関数がコンパイルされた後に `:breakadd` -で関数がデバッグ対象だとマークされたときを除いて、これは大体正しく行われます。 -必ず外側の関数がコンパイルされるより前にブレークポイントを定義するように気をつ -けてください。 - -「ループ中」の変数は一度しか存在しません。リストに入れた全てのクロージャは同じ -インスタンス、すなわち最後に 4 の値をもつインスタンスを参照します。これは効率 -的で、何回もループする場合でも同様です。もしそれぞれのクロージャでコンテキスト -を分けたいのであれば、コンテキストを定義するため関数を呼んでください: > - def GetClosure(i: number): func - var infunc = i - return () => infunc + def ClosureEg(n: number): void + var outloop: number = 0 # outloop がループの外で宣言されている! + for i in range(n) + outloop = i + flist[i] = (): number => outloop # クロージャは同じ変数を参照 + # する + endfor + echo range(n)->map((i, _) => flist[i]()) enddef + ClosureEg(4) # [3, 3, 3, 3] を表示する +< +リストに置かれたすべてのクロージャは同じインスタンスを参照し、最終的には 3 に +なる。 +ただし、変数がループ内で宣言されると、以下の例に示すように各クロージャは独自の +コピーを取得する: >vim9 + + vim9script var flist: list - for i in range(5) - flist[i] = GetClosure(i) - endfor - echo range(5)->map((i, _) => flist[i]()) - # 結果: [0, 1, 2, 3, 4] - -いくらかの場面で、特に旧来の Vim script のコンテキストから Vim9 のクロージャを -呼ぶとき、その評価は失敗するでしょう。 *E1248* - -Note スクリプトレベルにおいて、ループ変数はループの後では無効になります。これ -はループ変数が後で呼ばれるクロージャで使われている場合、例えばタイマーと組み合 -わせる場合でも同様です。これは |E1302| エラーを発生させます: > - for n in range(4) - timer_start(500 * n, (_) => { - echowin n - }) - endfor + def ClosureEg(n: number): void + for i in range(n) + var inloop: number = i # inloopはループ内で宣言されている + flist[i] = (): number => inloop # クロージャは各インループを + # 参照する + endfor + echo range(n)->map((i, _) => flist[i]()) + enddef + ClosureEg(4) # [0, 1, 2, 3] を表示する -ブロックを使って変数を定義し、その変数をクロージャで使う必要があります: > - for n in range(4) - { - var nr = n - timer_start(500 * n, (_) => { - echowin nr - }) - } - endfor +各クロージャに個別のコンテキストを設定する別の方法は、関数を呼び出してそれを定 +義することである: >vim9 + + vim9script + def GetClosure(i: number): func + 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]()) + enddef + ClosureEg(4) # Echoes [0, 1, 2, 3] +< *E1271* +クロージャは、それが定義されているコンテキストでコンパイルされなければならな +い。そうすることで、そのコンテキスト内の変数を参照できるようになる。これはほと +んどの場合正しく実行されるが、関数がコンパイル後に `:breakadd` でデバッグ用に +マークされている場合は例外である。外側の関数をコンパイルする前に、必ずブレーク +ポイントを定義すること。 + *E1248* +ローカル変数をキャプチャする Vim9 のクロージャを文字列に変換して実行した場合な +ど、状況によってはエラーが発生する。これは、文字列実行コンテキストが、クロー +ジャが定義された元のコンテキストからローカル変数にアクセスできないために発生す +る。例: >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 + enddef + F_1248() # Vim(call):E1248: Closure called from invalid context -タイマーにおいて `:echowindow` を使うのは便利です。メッセージはポップアップに -表示され、タイマーがトリガーされたときにユーザーが行っていることに干渉しませ -ん。 +Vim9 script では、ループ変数はループが終了した後は無効になる。 +例えば、このタイマーは 0 から 2 までを別々の行にエコーする。しかし、`:endfor` +の後に変数 "n" を使用すると、|E121| エラーが発生する: >vim9 + + vim9script + for n in range(3) + var nr: number = n + timer_start(1000 * n, (_) => { + echowindow nr + }) + endfor + try + echowindow n + catch + echo v:exception + endtry +< + Note: タイマーでは `:echowindow` を使用すると便利である。メッセージ + がポップアップに表示されるため、トリガー時にユーザーの操作を妨 + げない。 -旧来の関数から Vim9 の関数への変換 ~ +:function を :def に変換する~ *convert_legacy_function_to_vim9* -これらが旧来の関数から Vim9 の関数へ変換するために行われる必要のある変更の大部 -分です。 - -- `func` や `function` を `def` に変更する。 -- `endfunc` や `endfunction` を `enddef` に変更する。 -- 関数の引数に型をつける。 -- もし関数が何か返すのであれば、戻り値の型をつける。 -- コメントが " に代わって # で始まるように変更する。 - - 例えば旧来の Vim script の関数: > - func MyFunc(text) - " 関数の本体 - endfunc -< がこうなる: > - def MyFunc(text: string): number - # 関数の本体 + *convert_:function_to_:def* +`:function` を `:def` 関数に変換するには、多くの変更が必要である。以下にいくつ +か例を挙げる。 + +- 変数宣言に使用されている `let` を `var`、`const`、または `final` のいずれか + に変更し、各 |script-variable| から "s:" を削除する。 +- `func` または `function` を `def` に変更する。 +- `endfunc` または `endfunction` を `enddef` に変更する。 +- 各関数の引数に適切な型(または "any")を追加する。 +- 各 |function-argument| から "a:" を削除する。 +- |:func-range|、|:func-abort|、|:func-dict|、|:func-closure| などの不適切なオ + プションを削除する。 +- 関数が何かを返す場合は、戻り値の型を追加する。(理想的には、何も返さない場合 + は "void" を追加する。) +- 不要な場所から行継続のバックスラッシュを削除する。 +- |g:|、|b:|、|w:|、|t:|、または |l:| 変数に値を代入するための `let` を削除す + る。 +- Vim9 script 構文の |lambda| 式を書き直す (|vim9-lambda| を参照)。 +- コメントを " ではなく # (先頭に空白文字) で始めるように変更する。 +- 必要に応じて式に空白文字を挿入する (|vim9-white-space| を参照)。 +- 文字列連結に使用される "." を " .. " に変更する。(または、 + |interpolated-string| を使用する。) + +以下の旧来の Vim script と Vim9 script の例は、これらの違いをすべて示している。 +まず、旧来の 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)"コメントの前にスペースがない + endfunction + call Leg8(10)->popup_notification(#{time: 3000})" 'Result: 42' をポッ + " プアップする + +Vim9 scripti で同等のもの: >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) # #コメントの前にスペースが必要 enddef + Vim9(10)->popup_notification({time: 3000}) # 'Result: 42' をポップアッ + # プする -- 引数に使われる "a:" を削除する。例: > - return len(a:text) -< がこうなる: > - return len(text) - -- 変数の宣言に使われる `let` を `var` に変更する。 -- 変数への値の代入に使われる `let` を削除する。これは既に宣言されているローカ - ル変数と b: w: g: t: 変数が対象である。 - - 例えば旧来の Vim script の関数: > - let lnum = 1 - let lnum += 3 - let b:result = 42 -< がこうなる: > - var lnum = 1 - lnum += 3 - b:result = 42 - -- 式中の必要なところへホワイトスペースを挿入する。 -- 結合に使われる "." を ".." に変更する。 - - 例えば旧来の Vim script の関数: > - echo line(1).line(2) -< がこうなる: > - echo line(1) .. line(2) - -- 常に行継続にバックスラッシュが必要なわけではない: > - echo ['one', - \ 'two', - \ 'three' - \ ] -< がこうなる: > - echo ['one', - 'two', - 'three' - ] +< Note: この例では、次のことも示しています (`:def` 関数の外側で): + - 旧来の |#{}| から "#" を削除する - |vim9-literal-dict| を参照 + - `:call` の省略 (Vim9 script では不要だが許可されている) -式のオプションで関数を呼び出す ~ +expr オプションで :def 関数を呼び出す ~ *expr-option-function* -'foldexpr' などのいくつかのオプションの値は、値を取得するために評価される式で -す。評価には、かなりのオーバーヘッドが発生する可能性があります。オーバーヘッド -を最小限に抑え、オプション値を非常に単純に保つ 1 つの方法は、コンパイル済み関 -数を定義し、引数なしで呼び出すようにオプションを設定することです。例: > +'foldexpr' などのいくつかのオプションの値は、値を得るために評価される式である。 +この評価にはかなりのオーバーヘッドがかかることがある。オーバーヘッドを最小限に +抑え、オプションの値をシンプルに保つ 1 つの方法は、コンパイル済みの関数を定義 +し、それを引数なしで呼び出すオプションを設定することである。例: >vim9 + vim9script - def MyFoldFunc(): any - ... v:lnum の行の折り畳みレベルを計算する - return level + def MyFoldFunc(): string + # これは行頭 (^) に続いて数字、ピリオド、スペースまたはタブ、大文 + # 字が続き、次の行が空であるものにマッチする + return getline(v:lnum) =~ '^[[:digit:]]\.[[:blank:]][[:upper:]]' + && getline(v:lnum + 1)->empty() ? '>1' : '1' enddef - set foldexpr=s:MyFoldFunc() + set foldexpr=MyFoldFunc() + set foldmethod=expr + norm! zM +< + Warning: このスクリプトは、この vim9.txt ヘルプバッファの "Heading 1" レベル + に折り畳みを作成して適用する。(スクリプトを読み込んだ後、通常モード + で |zR|を使用すると、すべての折り畳むを開くことができる。) + ============================================================================== 4. 型 *vim9-types* - *E1008* *E1009* *E1010* *E1012* - *E1013* *E1029* *E1030* -以下の組み込み型がサポートされています: - bool - number - float - string - blob - list<{type}> - dict<{type}> - object<{type}> - job - channel - tuple<{type}> - tuple<{type}, {type}, ...> - tuple<...list<{type}>> - tuple<{type}, ...list<{type}>> - func - func: {type} - func({type}, ...) - func({type}, ...): {type} + +対応する内部 |v:t_TYPE| 変数とともに表示される以下の型がサポートされている。 + + 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 -これらの型は宣言において使えますが、いかなる単純値も実際に "void" 型を持つこと -はありません。void (例えば、戻り値のない関数) を使おうとするとエラーがでます。 -*E1031* *E1186* + *E1031* *E1186* +これらの型は宣言で使用できるが、単純な値は "void" 型を持つことはできない。void +を値として使用しようとするとエラーが発生する。例: >vim9 -配列型はありません。代わりに list<{type}> を使ってください。不変のリストに -対しては大量の細かいメモリを割り当てするのを避ける効率的な実装が使われます。 + vim9script + def NoReturnValue(): void + enddef + try + 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 + endtry +< *E1008* *E1009* *E1010* *E1012* +不正な宣言や型の不一致はエラーを引き起こす。以下は、E1008、E1009、E1010、E1012 +のエラーの例である: >vim9 + + vim9cmd var l: list + vim9cmd var l: list + vim9cmd var l: list = ['42'] +< +配列型はない。代わりに、リストまたはタプルを使用する。これらの型はリテラル (定 +数) にもなり得る。次の例では、[5, 6] はリストのリテラル、(7, ) はタプルのリテ +ラルである。エコーされたリストもリストのリテラルである: >vim9 + + vim9script + var l: list = [1, 2] + var t: tuple<...list> = (3, 4) + echo [l, t, [5, 6], (7, )] +< *tuple-type* -tuple 型は、多かれ少なかれ具体的な方法で宣言できる: +tuple 型は次の方法で宣言できる: > tuple |Number| 型の項目を 1 つ含む tuple tuple |Number| と |String| の 2 つの項目を含む tuple tuple |Number|、|Float| および |Boolean| 型の 3 つの @@ -1519,7 +1887,9 @@ tuple> |Number| 型の項目とそれに続く 0 個以 var myTuple: tuple<...list> = () < *vim9-func-declaration* *E1005* *E1007* -部分適用と関数は幾らかの方法で宣言することができます: + *vim9-partial-declaration* + *vim9-func-type* +関数 (または部分関数) は次の方法で宣言できる: func 任意の種類の関数参照。引数や戻り値への型チェッ クはない。 func: void 任意の数および型の引数で、戻り値はない。 @@ -1541,33 +1911,110 @@ func({type}, ?{type}, ...list<{type}>): {type} - 可変長引数のリストの型 - 戻り値の型 -もし戻り値の型が "void" なら、関数は値を返しません。 - -関数参照はそれが呼び出し側から見えない追加の引数および・あるいは辞書を保存して -いる場合、|Partial| にすることもできます。それらは同じように呼び出されるた -め、宣言も同じです。 +戻り値の型が "void" の場合、関数は値を返さない。 -`:type` を使ってカスタム型を定義できます: > - :type MyList list -ユーザー関数と似たように、後から追加される組み込み型との名前の衝突を避けるた -め、カスタム型は大文字から始まらなければなりません。 +参照は |Partial| にすることもできる。この場合、追加の引数や辞書が格納されるが、 +これらは呼び出し元からは参照できない。これらは同じ方法で呼び出されるため、宣言 +も同じである。この対話型の例では、円の半径を入力すると、partial を使用して面積 +を小数点以下 2 桁で返す: >vim9 -そしてクラスとインターフェイスも型として使えます: > - :class MyClass - :var mine: MyClass + vim9script + def CircleArea(pi: float, radius: float): float + 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.)" +< + *vim9-typealias-type* +カスタム型 (|typealias|) は `:type` で定義できる。ユーザー関数と同様に、大文字 +で始める必要がある (これにより、現在または将来の組み込み型との名前の衝突を回避 +できる)。以下の例は、完全平方のリストを作成し、|type()| (14、typealias) +と|typename()| を報告する: >vim9 - :interface MyInterface - :var mine: MyInterface + 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()}" +< + *E1105* +typealias 自体は文字列に変換できない: >vim9 - :class MyTemplate - :var mine: MyTemplate - :var mine: MyTemplate + vim9script + type Ln = list + const FAILS: func = (): string => { + echo $"{Ln}" # E1105: Cannot convert typealias to string + } +< + *vim9-class-type* *vim9-interface-type* + *vim9-object-type* +|class|、|object|、|interface| はすべて型として使用できる。以下の対話型の例は、 +float 値の入力を促し、2 つの異なる図形の面積を返す。また、クラス、オブジェク +ト、インターフェイスの |type()| と |typename()| も報告する: >vim9 - :class MyInterface - :var mine: MyInterface - :var mine: MyInterface -{not implemented yet} + vim9script + interface Shape + def InfoArea(): tuple + endinterface + class Circle implements Shape + 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 + endclass + const INPUT: float = "Enter a float value: "->input()->str2float() + echo "\nAreas of shapes:" + var myCircle: object = Circle.new(INPUT) + 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}" + endfor + echo "\n\t\ttype()\ttypename()\n\t\t------\t----------" + echo $"Circle\t\t{Circle->type()}\t{Circle->typename()}" + echo $"Square\t\t{Square->type()}\t{Square->typename()}" + echo $"Shape\t\t{Shape->type()}\t{Shape->typename()}" + echo $"MyCircle\t{myCircle->type()}\t{myCircle->typename()}" + echo $"MySquare\t{mySquare->type()}\t{mySquare->typename()}" + echo $"shapes\t\t{shapes->type()}\t{shapes->typename()}" +< + *vim9-enum-type* *vim9-enumvalue-type* +|enum| は型 (|v:t_enum|) として使用できる。enum 値を保持する変数は、実行時に +enumvalue 型 (|v:t_enumvalue|) を持つ。以下の対話型の例は、文字の入力を促し、 +正方形または菱形に関する情報を返す。また、enum と enumvalue の |type()| と +|typename()| も報告する: >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 + endenum + echo "Rhombus (r) or Square (s)?" + var myQuad: Quad = getcharstr() =~ '\c^R' ? Quad.Rhombus : Quad.Square + echo myQuad.string() .. "\ttype()\ttypename()" + echo $"Quad \t{Quad->type()} \t{Quad->typename()}" + echo $"myQuad\t{myQuad->type()}\t{myQuad->typename()}" +< + Notes: このスクリプトは組み込みメソッド "string()" (|object-string()|) + を使用している。 + Quad と myQuad の typename() は同じ ("enum") ですが、 + type() は区別される (myQuad は 16、enumvalue を返すが、Quad は + 15、enum を返す)。 変数の型と型キャスト ~ *variable-types* @@ -1666,7 +2113,7 @@ Vim9 script ではこれは厳格にされています。使われている値 る場合は、ほとんどの場所で前と同じように動作します。時々エラーになり、それゆえ 後方互換性が壊れています。例: - 真偽値が期待されるところで、値が 0 か 1 でない数値を使う。 *E1023* -- 数値のオプションを設定するときに文字列を使う。 *E1024* *E1105* +- 数値のオプションを設定するときに文字列を使う。 *E1024* - 文字列が期待されるところで数値を用いる。 一つの影響として、型が宣言された場合は |map()| に渡されたリストか辞書の要素の diff --git a/en/vim9.txt b/en/vim9.txt index 7941b83ab..ad8a4d475 100644 --- a/en/vim9.txt +++ b/en/vim9.txt @@ -24,11 +24,12 @@ features in Vim9 script. ------------------------------------------------------------------------------ NOTE: In this vim9.txt help file, the Vim9 script code blocks beginning - with `vim9script` are Vim9 script syntax highlighted. Also, they are - sourceable, meaning you can run them to see what they output. To - source them, use `:'<,'>source` (see |:source-range|), which is done - by visually selecting the line(s) with |V| and typing `:so`. - For example, try it on the following Vim9 script: >vim9 + 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!" @@ -63,7 +64,7 @@ dictionary adds quite a lot of overhead. In a Vim9 function this dictionary is not available. Other differences are more subtle, such as how errors are handled. -The Vim9 script syntax and semantics are used in: +Vim9 script syntax, semantics, and behavior apply in: - a function defined with the `:def` command - a script file where the first command is `vim9script` - an autocommand defined in the context of the above @@ -77,18 +78,72 @@ 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* *E1164* - Evaluate and execute {cmd} using Vim9 script syntax and - semantics. Useful when typing a command and in a legacy - script or function. +: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. + + 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* + `:vim9cmd` cannot stand alone; it must be followed by a command. + +: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 + "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* + 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* + `:legacy` cannot stand alone; it must be followed by a command. -:leg[acy] {cmd} *:leg* *:legacy* *E1189* *E1234* - Evaluate and execute {cmd} using legacy script syntax and - semantics. Only useful in a Vim9 script or a :def function. - Note that {cmd} cannot use local variables, since it is parsed - with legacy expression syntax. -See some examples of Vim9 script at |52.6|. ============================================================================== 2. Differences from legacy Vim script *vim9-differences* @@ -103,7 +158,8 @@ script and `:def` functions; details are below: echo "hello " .. yourName .. ", how are you?" -- White space is required in many places to improve readability. +- White space is required in many places to improve readability, + see |vim9-white-space|. - Assign values without `:let` *E1126* , declare variables with `:var`: > var count = 0 count += 3 @@ -231,7 +287,7 @@ You can call a legacy dict function though: > var d = {func: Legacy, value: 'text'} d.func() enddef -< *E1096* *E1174* *E1175* + The argument types and return type need to be specified. The "any" type can be used, type checking will then be done at runtime, like with legacy functions. @@ -274,7 +330,7 @@ script "export" needs to be used for those to be used elsewhere. > def ThisFunction() # script-local def g:ThatFunction() # global export def Function() # for import and import autoload -< *E1058* *E1075* +< *E1075* When using `:function` or `:def` to specify a nested function inside a `:def` function and no namespace was given, this nested function is local to the code block it is defined in. It cannot be used in `function()` with a string @@ -841,7 +897,7 @@ Notes: White space ~ - *E1004* *E1068* *E1069* *E1074* *E1127* *E1202* + *vim9-white-space* *E1004* *E1068* *E1069* *E1074* *E1127* *E1202* Vim9 script enforces proper use of white space. This is no longer allowed: > var name=234 # Error! var name= 234 # Error! @@ -1235,69 +1291,245 @@ subtracting one: > Using ++var or --var in an expression is not supported yet. + ============================================================================== 3. New style functions *fast-functions* - *:def* *E1028* + *:def* :def[!] {name}([arguments])[: {return-type}] Define a new function by the name {name}. The body of the function follows in the next lines, until the - matching `:enddef`. *E1073* - *E1011* + matching `:enddef`. + *E1073* + The {name} cannot be reused at the script-local level: >vim9 + + vim9script + def F_1073() + enddef + def F_1073() # E1073: Name already defined: ... + enddef +< *E1011* The {name} must be less than 100 bytes long. - *E1003* *E1027* *E1056* *E1059* - The type of value used with `:return` must match - {return-type}. When {return-type} is omitted or is - "void" the function is not expected to return - anything. - *E1077* *E1123* + + *E1077* {arguments} is a sequence of zero or more argument declarations. There are three forms: {name}: {type} {name} = {value} {name}: {type} = {value} - The first form is a mandatory argument, the caller - must always provide them. - The second and third form are optional arguments. - When the caller omits an argument the {value} is used. + The first form is a mandatory argument. So, the + declaration must provide a type. Example: >vim9 + vim9script + def F_1077(x): void + # 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 + + vim9script + def SecondForm(arg = "Hi"): void + 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}' + enddef + ThirdForm() +< *E1123* + Arguments in a builtin function called in a `:def` + 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) + 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 + + vim9script + def F_1003(): bool + return # E1003: Missing return value + enddef + F_1003() +< >vim9 + vim9script + def F_1027(): bool + echo false # E1027: Missing return statement + enddef + F_1027() +< >vim9 + vim9script + def F_1096(): void + return false # E1096: Returning a value ... + 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 + + def F_1056(): + # E1056: Expected a type: + enddef + def F_1059() : bool + # E1059: No white space allowed before colon:... + enddef +< The function will be compiled into instructions when - called, or when `:disassemble` or `:defcompile` is - used. Syntax and type errors will be produced at that - time. + called or when either `:defcompile` or `:disassemble` is + used. (For an example, see |:disassemble|.) Syntax + and type errors will be produced at that time. + *E1058* It is possible to nest `:def` inside another `:def` or - `:function` up to about 50 levels deep. + `:function` only up to 49 levels deep. At 50 or more + levels, it is a |E1058| error. + *E1117* - [!] is used as with `:function`. Note that - script-local functions cannot be deleted or redefined - later in Vim9 script. They can only be removed by - reloading the same script. + [!] is allowed only in legacy Vim script because it + permits function redefinition (as with `:function`!). + 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 + + " Legacy Vim script :def! example + def! LegacyFunc() + 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 + enddef + Func() +< >vim9 + vim9script + def! F_477(): void # E477: No ! allowed + enddef +< >vim9 + vim9script + def F_1084(): void + enddef + delfunction! F_1084 + # E1084: Cannot delete Vim9 script function F_1084 +< + Note: The generic error *E1028* ("Compiling :def + function failed") indicates an undeterminable error + during compilation. If reproducible, it may be + reported at https://github.com/vim/vim/issues as + it could represent a gap in Vim's error reporting. *:enddef* *E1057* *E1152* *E1173* -:enddef End of a function defined with `:def`. It should be on - a line by its own. +:enddef End of a function defined with `:def`. It should be on + a line by itself. Examples: >vim9 + vim9script + def MyFunc() + echo 'Do Something' | enddef + # E1057: Missing :enddef +< >vim9 + vim9script + def F_1173() + enddef echo 'X' + # E1173: Text found after enddef: echo 'X' +< >vim9 + vim9script + def F_1152() + 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 function is defined in is Vim9 script, then script-local -variables can be accessed without the "s:" prefix. They must be defined -before the function is compiled. If the script the function is defined in is -legacy script, then script-local variables must be accessed with the "s:" -prefix if they do not exist at the time of compiling. - *E1269* -Script-local variables in a |Vim9| script must be declared at the script -level. They cannot be created in a function, also not in a legacy function. +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 + + 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 + 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 + enddef + let s:unus = 1 + call MyLegacyDef() " Calls MyLegacyDef() and compiles if not already + let s:duo = 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 + vim9script + function F_1269() + 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[ompile] Compile functions and classes (|class-compile|) defined in the current script that were not compiled yet. This will report any errors found during compilation. -:defc[ompile] MyClass Compile all methods in a class. |class-compile| + 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 + + 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.) :defc[ompile] {func} :defc[ompile] debug {func} @@ -1305,16 +1537,35 @@ level. They cannot be created in a function, also not in a legacy function. Compile function {func}, if needed. Use "debug" and "profile" to specify the compilation mode. This will report any errors found during compilation. - {func} call also be "ClassName.functionName" to + {func} can also be "ClassName.functionName" to compile a function or method in a class. - {func} call also be "ClassName" to compile all + {func} can also be "ClassName" to compile all functions and methods in a class. *:disa* *:disassemble* :disa[ssemble] {func} Show the instructions generated for {func}. - This is for debugging and testing. *E1061* - Note that for command line completion of {func} you - can prepend "s:" to find script-local functions. + This is for debugging and testing. + If {func} is not found, error *E1061* occurs. + {func} can also be "ClassName.functionName" to + disassemble a function in a class. + 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 + + vim9script + class Line + 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->_ :disa[ssemble] profile {func} Like `:disassemble` but with the instructions used for @@ -1324,192 +1575,304 @@ level. They cannot be created in a function, also not in a legacy function. Like `:disassemble` but with the instructions used for debugging. + Note: For command line completion of {func}, script-local functions + are shown with their . Depending on options, including + |wildmenumode()|, completion may work with "s:", " - def MapList(): list - var list = ['aa', 'bb', 'cc', 'dd'] - return range(1, 2)->map('list[v:val]') - enddef +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 + 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 + enddef + echo 'script local'->MapList() # Echoes ['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, use a lambda: > +function scope. Instead, in Vim9 script, use a lambda: >vim9 + + vim9script def MapList(): list - var list = ['aa', 'bb', 'cc', 'dd'] - return range(1, 2)->map((_, v) => list[v]) + const DEF_LOCAL: list = ['A', 'def-local', 'list'] + return [1]->map((_, v) => DEF_LOCAL[v]) enddef + echo MapList() # Echoes ['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 -For commands that are not compiled, such as `:edit`, backtick expansion can be -used and it can use the local scope. Example: > - def Replace() - var fname = 'blah.txt' - edit `=fname` + vim9script + def EditNewBlah() + var fname: string = 'blah.txt' + split + edit `=fname` enddef + EditNewBlah() # A new split is created as buffer 'blah.txt' +< +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 -Closures defined in a loop will share the same context. For example: > + vim9script var flist: list - for i in range(5) - var inloop = i - flist[i] = () => inloop - endfor - echo range(5)->map((i, _) => flist[i]()) - # Result: [4, 4, 4, 4, 4] + 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]()) + enddef + ClosureEg(4) # Echoes [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 + + 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]()) + enddef + ClosureEg(4) # Echoes [0, 1, 2, 3] + +Another way to have a separate context for each closure is to call a +function to define it: >vim9 + + vim9script + def GetClosure(i: number): func + 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]()) + enddef + ClosureEg(4) # Echoes [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, except -when a function is marked for debugging with `:breakadd` after it was compiled. -Make sure to define the breakpoint before compiling the outer function. - -The "inloop" variable will exist only once, all closures put in the list refer -to the same instance, which in the end will have the value 4. This is -efficient, also when looping many times. If you do want a separate context -for each closure, call a function to define it: > - def GetClosure(i: number): func - var infunc = i - return () => infunc +variables in that context can be found. This mostly happens correctly, +except when a function is marked for debugging with `:breakadd` after it was +compiled. Make sure to define the breakpoint before compiling the outer +function. + *E1248* +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 + + vim9script + def F_1248(): void + 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 - var flist: list - for i in range(5) - flist[i] = GetClosure(i) - endfor - echo range(5)->map((i, _) => flist[i]()) - # Result: [0, 1, 2, 3, 4] - -In some situations, especially when calling a Vim9 closure from legacy -context, the evaluation will fail. *E1248* - -Note that at the script level the loop variable will be invalid after the -loop, also when used in a closure that is called later, e.g. with a timer. -This will generate error |E1302|: > - for n in range(4) - timer_start(500 * n, (_) => { - echowin n - }) - endfor +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 -You need to use a block and define a variable there, and use that one in the -closure: > - for n in range(4) - { - var nr = n - timer_start(500 * n, (_) => { - echowin nr - }) - } + vim9script + for n in range(3) + var nr: number = n + timer_start(1000 * n, (_) => { + echowindow nr + }) endfor - -Using `:echowindow` is useful in a timer, the messages go into a popup and will -not interfere with what the user is doing when it triggers. + try + echowindow n + catch + 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. -Converting a function from legacy to Vim9 ~ +Converting a :function to a :def~ *convert_legacy_function_to_vim9* -These are the most changes that need to be made to convert a legacy function -to a Vim9 function: + *convert_:function_to_:def* +There are many changes that need to be made to convert a `:function` to +a `:def` function. The following are some of them: +- Change `let` used to declare variables to one of `var`, `const`, or `final`, + and remove the "s:" from each |script-variable|. - Change `func` or `function` to `def`. - Change `endfunc` or `endfunction` to `enddef`. -- Add types to the function arguments. -- If the function returns something, add the return type. -- Change comments to start with # instead of ". +- Add the applicable type (or "any") to each function argument. +- Remove "a:" from each |function-argument|. +- Remove inapplicable options such as |:func-range|, |:func-abort|, + |:func-dict|, and |:func-closure|. +- 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. +- 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|). +- Change "." used for string concatenation to " .. ". (Alternatively, use + an |interpolated-string|.) + +The following legacy Vim script and Vim9 script examples demonstrate all +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 + endfunction + call Leg8(10)->popup_notification(#{time: 3000})" Pops up 'Result: 42' + +The equivalent in Vim9 script: >vim9 - For example, a legacy function: > - func MyFunc(text) - " function body - endfunc -< Becomes: > - def MyFunc(text: string): number - # function body + 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 enddef + Vim9(10)->popup_notification({time: 3000}) # Pops up 'Result: 42' -- Remove "a:" used for arguments. E.g.: > - return len(a:text) -< Becomes: > - return len(text) - -- Change `let` used to declare a variable to `var`. -- Remove `let` used to assign a value to a variable. This is for local - variables already declared and b: w: g: and t: variables. - - For example, legacy function: > - let lnum = 1 - let lnum += 3 - let b:result = 42 -< Becomes: > - var lnum = 1 - lnum += 3 - b:result = 42 - -- Insert white space in expressions where needed. -- Change "." used for concatenation to "..". - - For example, legacy function: > - echo line(1).line(2) -< Becomes: > - echo line(1) .. line(2) - -- line continuation does not always require a backslash: > - echo ['one', - \ 'two', - \ 'three' - \ ] -< Becomes: > - echo ['one', - 'two', - 'three' - ] +< 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) -Calling a function in an expr option ~ +Calling a :def function in an expr option ~ *expr-option-function* 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 very -simple, is to define a compiled function and set the option to call it -without arguments. Example: > +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 + vim9script - def MyFoldFunc(): any - ... compute fold level for line v:lnum - return level + 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' enddef - set foldexpr=s:MyFoldFunc() + set foldexpr=MyFoldFunc() + set foldmethod=expr + norm! 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.) + ============================================================================== 4. Types *vim9-types* - *E1008* *E1009* *E1010* *E1012* - *E1013* *E1029* *E1030* -The following builtin types are supported: - bool - number - float - string - blob - list<{type}> - dict<{type}> - object<{type}> - job - channel - tuple<{type}> - tuple<{type}, {type}, ...> - tuple<...list<{type}>> - tuple<{type}, ...list<{type}>> - func - func: {type} - func({type}, ...) - func({type}, ...): {type} + +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| void -These types can be used in declarations, but no simple value will actually -have the "void" type. Trying to use a void (e.g. a function without a -return value) results in error *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 -There is no array type, use list<{type}> instead. For a list constant an -efficient implementation is used that avoids allocating a lot of small pieces -of memory. + vim9script + def NoReturnValue(): void + enddef + try + 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 + 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'] +< +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 + + vim9script + var l: list = [1, 2] + var t: tuple<...list> = (3, 4) + echo [l, t, [5, 6], (7, )] +< *tuple-type* -A tuple type can be declared in more or less specific ways: +A tuple type may be declared in the following ways: tuple a tuple with a single item of type |Number| tuple a tuple with two items of type |Number| and |String| @@ -1535,7 +1898,9 @@ variadic tuple must end with a list type. Examples: > var myTuple: tuple<...list> = () < *vim9-func-declaration* *E1005* *E1007* -A partial and function can be declared in more or less specific ways: + *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 @@ -1567,29 +1932,108 @@ If the return type is "void" the function does not return a value. The reference can also be a |Partial|, in which case it stores extra arguments and/or a dictionary, which are not visible to the caller. Since they are -called in the same way the declaration is the same. - -Custom types can be defined with `:type`: > - :type MyList list -Custom types must start with a capital letter, to avoid name clashes with -builtin types added later, similarly to user functions. +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 -And classes and interfaces can be used as types: > - :class MyClass - :var mine: MyClass + vim9script + def CircleArea(pi: float, radius: float): float + 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.)" +< + *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 - :interface MyInterface - :var mine: MyInterface + 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()}" +< + *E1105* +A typealias itself cannot be converted to a string: >vim9 - :class MyTemplate - :var mine: MyTemplate - :var mine: MyTemplate + vim9script + type Ln = list + const FAILS: func = (): string => { + 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 - :class MyInterface - :var mine: MyInterface - :var mine: MyInterface -{not implemented yet} + vim9script + interface Shape + def InfoArea(): tuple + endinterface + class Circle implements Shape + 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 + endclass + const INPUT: float = "Enter a float value: "->input()->str2float() + echo "\nAreas of shapes:" + var myCircle: object = Circle.new(INPUT) + 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}" + endfor + echo "\n\t\ttype()\ttypename()\n\t\t------\t----------" + echo $"Circle\t\t{Circle->type()}\t{Circle->typename()}" + echo $"Square\t\t{Square->type()}\t{Square->typename()}" + echo $"Shape\t\t{Shape->type()}\t{Shape->typename()}" + echo $"MyCircle\t{myCircle->type()}\t{myCircle->typename()}" + echo $"MySquare\t{mySquare->type()}\t{mySquare->typename()}" + echo $"shapes\t\t{shapes->type()}\t{shapes->typename()}" +< + *vim9-enum-type* *vim9-enumvalue-type* +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 + 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 + endenum + echo "Rhombus (r) or Square (s)?" + var myQuad: Quad = getcharstr() =~ '\c^R' ? Quad.Rhombus : Quad.Square + echo myQuad.string() .. "\ttype()\ttypename()" + 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). Variable types and type casting ~ *variable-types*