VCIにおける、アノテーションの書き方(VSCode:EmmyLua)

# はじめに
今回の記事は[Virtual Cast Wiki](https://virtualcast.jp/wiki/doku.php)の[VCIスクリプトの開発環境の導入](https://virtualcast.jp/wiki/doku.php?id=vscode:tutorial)をチュートリアル通りに実施した場合における、アノテーションの書き方やうまく動かない場合の対処法について解説する記事です。

#### インテリセンスはこのように動きます。![AnnotationDemo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/7470b525-0dc3-cf75-a097-9584712e8087.gif)
#### マウスオーバーで情報が表示されます。![MouseOverDemo.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150633/f1638a05-eddd-4f0e-6025-10e332aafcb6.gif)
#### 今回使うluaスクリプト
指し棒を伸ばしたり縮めたりするスクリプトです。伸ばすがOpenで縮めるがClose。

```lua:ExpandableBaton.lua
---@class ExBaton_Const 固定値
---@field SubItem_Baton string @SubItem名
---@field CloseAnimation string @Open状態から閉じる時のアニメーション
---@field OpenAnimation string @Close状態から開く時のアニメーション
---@field ThrottleTick string @vci.state用。
---@field IsOpen string @初期値はtrue,しまっている時はfalse
local ExBaton_Const = {
    SubItem_Baton = "feath_Baton",
    CloseAnimation = "BatonClose",
    OpenAnimation = "BatonOpen",
    ThrottleTick = "ThrottleTick",
    IsOpen = "IsOpen"
}

---@class ExBaton_Class
---@field ThrottleTick number @Useボタン押下時の時間保持用変数
local ExBaton_Class = {}

function ExBaton_Class.new()
    ---@type ExBaton_Class
    local obj = {}
    if vci.assets.IsMine then
        vci.state.Set(ExBaton_Const.IsOpen,true)
    end
    obj.ThrottleTick = vci.me.Time.Ticks
    setmetatable(obj, {__index = ExBaton_Class})
    return obj
end

---Batonの状態に応じて閉じていれば開き、開いていれば閉じる。
---また、一度実行したら1秒間再実施不可
function ExBaton_Class:ChangeState()
    if vci.me.Time.Ticks > self.ThrottleTick then
        if vci.state.Get(ExBaton_Const.IsOpen) then
            vci.assets._ALL_PlayAnimationFromName(ExBaton_Const.CloseAnimation,false)
            vci.state.Set(ExBaton_Const.IsOpen,false)
        else
            vci.assets._ALL_PlayAnimationFromName(ExBaton_Const.OpenAnimation,false)
            vci.state.Set(ExBaton_Const.IsOpen,true)
        end
        self.ThrottleTick = vci.me.Time.Ticks + TimeSpan.FromSeconds(1).Ticks
    end
end

local ExBaton_Cls = ExBaton_Class.new()

---[SubItemの所有権&Use状態]アイテムをグラッブしてグリップボタンを押すと呼ばれる。
---@param use string @押されたアイテムのSubItem名
function onUse(use)
    if use == ExBaton_Const.SubItem_Baton then
        ExBaton_Cls:ChangeState()
    end
end
```

#第0章 始めにつまづきやすいトラブルシューティング
[Virtual Cast Wiki](https://virtualcast.jp/wiki/doku.php)の[VCIスクリプトの開発環境の導入](https://virtualcast.jp/wiki/doku.php?id=%E3%83%A2%E3%83%87%E3%83%AB%E4%BD%9C%E6%88%90:vci%E4%BD%9C%E6%88%90:vci%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%97%E3%83%88%E3%81%AE%E9%96%8B%E7%99%BA%E7%92%B0%E5%A2%83%E3%81%AE%E5%B0%8E%E5%85%A5)をチュートリアル通りに実施した際に発生しやすい、うまくインテリセンスが機能しない問題点と改善方法を2つ挙げていきます。
とりあえず目を通していただいて、問題が発生した際に思い出してください。

## 全く関係のないインテリセンスが表示される。
チュートリアル通りに、`EMBEDDEDSCRIPTWORKSPACE`をワークスペースにすると、これまで読み込んだ公開VCIや自分の作成したVCIのグローバル変数全てのインテリセンスが出てきます。

他の方の作成した変数と被らないように、対策として一番初めの数文字を今回専用の文字としてつなげるようにします。
今回は`ExBaton_`がそれにあたります。
それと、インテリセンス汚染を発生させないためにも作成する変数は全てlocal変数にしましょう。

## main.luaと_main.luaで競合することがある
luaスクリプトを修正するにあたって、`_main.lua`ファイルを`main.lua`に書き直してプログラムを書き直してデバッグを実施することは当然ながらよくあるかと思います。
その際に`_main.lua`ファイルと`main.lua`ファイルが共存することになりますが、この時に`main.lua`ファイルにアノテーションをいくら追加しても反映されないことがあります。
簡単な話で、既に`_main.lua`ファイル側のアノテーションを読み込んでいるがために`main.lua`ファイルに書いた内容が無視されるのです。
この場合は一度`_main.lua`ファイルを削除して反映されているかを確認してください。
また、
右クリック＞定義へ移動 F12
を実施する際に`main.lua`ではなく`_main.lua`側に飛ばされることもよくあるので注意してください。（6敗）
#第1章 固定値クラスの作成
まず、参考サイトとして
[Annotations — EmmyLua for IntelliJ IDEA 1.0 documentation](https://emmylua.github.io/annotation.html)
を挙げさせていただきます。こちらを読んで分かれば以下は無視してOKです。

##クラス名のアノテーション
クラス名には、`---@class`を用います。コロン`:`を用いることで、インテリセンスを継承させることができます。

```lua:Class.lua
---Full Format
---@class MY_TYPE[:PARENT_TYPE] [comment]

---実際に使うとこんな感じ
---@class ExBaton_Const 固定値
```

##フィールドのアノテーション
フィールドには、`---@field`を用います。最低限の機能を用いたいならば`public`や`private`の記述はなくてもいいかな。
パイプライン`|`を用いることで、複数のタイプ(`string`や`number`)を指定できます。

```lua:Field.lua
---Full Format
---@field [public|protected|private] field_name FIELD_TYPE[|OTHER_TYPE] [@comment]

---実際に使うとこんな感じ
---@field CloseAnimation string @Open状態から閉じる時のアニメーション
```

##実例
```lua:Const.lua
---@class ExBaton_Const 固定値
---@field SubItem_Baton string @SubItem名
---@field CloseAnimation string @Open状態から閉じる時のアニメーション
---@field OpenAnimation string @Close状態から開く時のアニメーション
---@field ThrottleTick string @vci.state用。
---@field IsOpen string @初期値はtrue,しまっている時はClose
local ExBaton_Const = {
    SubItem_Baton = "feath_Baton",
    CloseAnimation = "BatonClose",
    OpenAnimation = "BatonOpen",
    ThrottleTick = "ThrottleTick",
    IsOpen = "IsOpen"
}
```

##ネストを深くしたいなら
固定値の種類に応じてクラス分けをしたいこともあるかと思います。
その場合は、Constクラスの更に下にクラスを作成すると良いです。例を参考にしてね。
数値を固定値にする際はコメントにその数値を書くとあとでマウスオーバーで数値を確認できます。

```lua:FL_Const.lua
---@class FL_Const 固定値
---@field StateName FL_Const.StateName @ステートマシンにおける状態名格納クラス
---@field NUM FL_Const.NUM @固定数値
local FL_Const = {
    ---@class FL_Const.StateName ステートマシンにおける状態名格納クラス
    ---@field Wait string @操作可能状態
    ---@field Falling string
    ---@field Connecting string @消す対象を決める
    ---@field Vanishing string @実際に消す
    ---@field Check string
    ---@field NextTurn string @Wait移行のための前準備
    ---@field Initialize string @初期化処理前状態
    ---@field Gameover string @rows=12,cols=3が埋まった時
    StateName = {
        Wait = "Wait",
        Falling = "Falling",
        Connecting = "Connecting",
        Vanishing = "Vanishing",
        Check = "Check",
        NextTurn = "NextTurn",
        Init = "Initialize",
        Gameover = "Gameover"
    },
    ---@class FL_Const.NUM 固定数値
    ---@field COLS number @Fieldの横のボール数:6
    ---@field ROWS number @Fieldの縦のボール数:13
    ---@field HIDE number @Fieldの隠れているボール行:1
    ---@field VANISHCOUNT number @いくつ縦横繋がると消えるか:4
    ---@field COLOR_COUNT number @有色の種類:4
    ---@field LOWCOLORNUM number @COLOR_NUMで使う有色で番号:2
    ---@field HIGHCOLORNUM number @COLOR_NUMで使う色番号:5
    ---@field BALL_SIZE number @BALLの大きさ1=1m:0.1
    ---@field COLOR_LOOP number @128ターンで配列が１週する:128
    ---@field NEXT_BALL number @予告表示ターン数:2
    ---@field PAIR number @1組2つのボールを動かす:2
    NUM = {
        COLS = 6,
        ROWS = 13,
        HIDE = 1,
        VANISHCOUNT = 4,
        COLOR_COUNT = 4,
        LOWCOLORNUM = 2,
        HIGHCOLORNUM = 5,
        BALL_SIZE = 0.1,
        COLOR_LOOP = 128,
        NEXT_BALL = 2,
        PAIR = 2,
    }
}
```

#第2章 関数の作成
クラスで用いる関数(メソッド)ならばlocalを付ける必要はないですが、クラスを用いずに関数を作成する場合はlocalを記載しましょう。
まずはアノテーションについて説明します。

##引数のアノテーション
引数には、`---@param`を用います。
パイプライン`|`を用いることで、複数のタイプ(`string`や`number`)を指定できます。

```lua:Parameter.lua
---Full Format
---@param param_name MY_TYPE[|other_type] [@comment]

---itemAとitemBを交換します
---@param itemA string
---@param itemB string
local function ControlChange(itemA,itemB)
    ...
end
```
##返り値のアノテーション
返り値には、`---@return`を用います。
パイプライン`|`を用いることで、複数のタイプ(`string`や`number`)を指定できます。

```lua:Parameter.lua
---Full Format
---@return MY_TYPE[|OTHER_TYPE] [@comment]

---引数の倍の値を返します。
---@param num number
---@return number
local function ReturnTwice(num)
    return num * 2
end
```

##実例
関数のコメントは、直前の`---`後に`@`をつけずに記載した内容がコメントになります。

```lua:Function.lua
---引数の二乗を返します。
---@param num number
---@return number
local function ReturnSquares(num)
    return num * num
end
```

#第3章 クラスの作成
実例をまず見せて、それを分割して解説していきます。

```lua:Class.lua

--1:クラスとクラス内変数のアノテーションの記入、及びクラスの宣言
---@class ExBaton_Class
---@field ThrottleTick number @Useボタン押下時の時間保持用変数
local ExBaton_Class = {}

--2:コンストラクタの作成
function ExBaton_Class.new()
    ---@type ExBaton_Class
    local obj = {}
    if vci.assets.IsMine then
        vci.state.Set(ExBaton_Const.IsOpen,true)
    end
    obj.ThrottleTick = vci.me.Time.Ticks
    setmetatable(obj, {__index = ExBaton_Class})
    return obj
end

--3:メソッドの作成。メソッドが複数ある場合は複数この下に作成。
---Batonの状態に応じて閉じていれば開き、開いていれば閉じる。
---また、一度実行したら1秒間再実施不可
function ExBaton_Class:ChangeState()
    if vci.me.Time.Ticks > self.ThrottleTick then
        if vci.state.Get(ExBaton_Const.IsOpen) then
            vci.assets._ALL_PlayAnimationFromName(ExBaton_Const.CloseAnimation,false)
            vci.state.Set(ExBaton_Const.IsOpen,false)
        else
            vci.assets._ALL_PlayAnimationFromName(ExBaton_Const.OpenAnimation,false)
            vci.state.Set(ExBaton_Const.IsOpen,true)
        end
        self.ThrottleTick = vci.me.Time.Ticks + TimeSpan.FromSeconds(1).Ticks
    end
end

--4:クラスのインスタンスを生成します。
local ExBaton_Cls = ExBaton_Class.new()
```

##1:クラスとクラス内変数のアノテーションの記入、及びクラスの宣言
まずクラス用の変数にアノテーションを追加します。そのクラスには空のテーブルを代入します。

```lua:Class1.lua
--1:クラスとクラス内変数のアノテーションの記入、及びクラスの宣言
---@class ExBaton_Class
---@field ThrottleTick number @Useボタン押下時の時間保持用変数
local ExBaton_Class = {}
```

##2:コンストラクタの作成
コンストラクタ用のメソッドを作成します。
`---@type`アノテーションは初出なので解説しますが、記載されている次の変数に記載した型がインテリセンスに反映されます。
`---@type`アノテーションについては
[@type type annotation — EmmyLua for IntelliJ IDEA 1.0 documentation](https://emmylua.github.io/annotations/type.html)
を見たほうがわかりやすいです。

```lua:Class2.lua
--2:コンストラクタの作成
function ExBaton_Class.new()

    ---@type ExBaton_Class
    local obj = {}
    --ここから初期化宣言
    if vci.assets.IsMine then
        vci.state.Set(ExBaton_Const.IsOpen,true)
    end
    obj.ThrottleTick = vci.me.Time.Ticks
    --ここまで初期化宣言
    --ここから下はコピペでOK(クラス名だけ作成したクラス名に変更)
    setmetatable(obj, {__index = ExBaton_Class})
    return obj
end
```

##3:メソッドの作成。メソッドが複数ある場合は複数この下に作成。
メソッドについては関数とほぼ同一なので省略。
ドット`.`とコロン`:`は他言語をメインにしている方は特に注意が必要です。(10敗)

##4:クラスのインスタンスを生成します。
一般的なクラスのインスタンスの生成です。
`local`と宣言することを忘れないようにしましょう。

```lua:Class4.lua
--4:クラスのインスタンスを生成します。
local ExBaton_Cls = ExBaton_Class.new()
```

#第4章 型の書き方
配列などの書き方についてざっくり解説します。

##配列
配列には`[]`を記載します。

```lua:Array.lua
---Full Format
---@type MY_TYPE[]

---@type number[]
local Fibonacci = {}
```

##テーブル
テーブルには`table<KEY_TYPE, VALUE_TYPE>`を記載します。

```lua:Table.lua
---Full Format
---@type table<KEY_TYPE, VALUE_TYPE>

---@type table<string,Color>
local ColorCode = {}
```

##関数
関数には`fun(param:MY_TYPE):RETURN_TYPE`を記載します。

```lua:Function.lua
---Full Format
---@type fun(param:MY_TYPE):RETURN_TYPE

---@type fun(key:string):void
local addKey
```

#最後に
基本的なアノテーションの書き方については網羅した方と思いますが、より細かい点を知りたい場合は
[Annotations — EmmyLua for IntelliJ IDEA 1.0 documentation](https://emmylua.github.io/annotation.html)
こちらを参考にすると良いです。
皆さんにもよりよいインテリセンスライフを。