公開技術情報

[English] [Japanese]

A.イマドキな開発環境でもっと楽しよう

皆さんは Lua の開発に、エディタは何を使用しているでしょうか?

Lua Development Tools や Atom, VSCode のようなイマドキな機能満載な環境ですか? それとも、emacs や vim などの古典的な環境ですか?

どちらの環境にしろ、 Lua コーディング中の補完機能に不満を持っていないですか?

Lua の補完機能は、多くの場合かなり賢く補完してくれるんですが、 補完が効かないことって結構ありますよね?

ちょっと確認しただけでも、次の場面では補完が効いてくれないようです。

  • 関数の引数に渡したオブジェクトが持つメソッドのフィールド補完
  • metatable を利用したオブジェクト指向プログラミングの補完

軽い処理では、上記問題はあまり気にならないのかもしれないですが、 ある程度の規模の処理を書く時は結構気になります。

Lua のトランスコンパイラ LuneScript では、 どんな場面でも補完が効くように、コンパイラレベルで補完機能を提供します。

ここでは、LuneScript のコーディング時に利用出来る、 イマドキの開発支援を紹介します。

具体的には次の機能です。

  • オートインデント
  • コード補完機能
  • カーソル位置の型情報確認
  • Syntax エラーチェック機能
  • subfile を認識した検索

オートインデント

オートインデントは、コーディングする際の最も基本的なサポート機能です。

LuneScript では、オートインデントを行なうフォーマッタを提供します。

emacs の環境では、この後に示す lns-mode 設定を実施するだけで オートインデントを利用できます。

emacs 以外の環境は、現状サポートしていません。 ですが、オートインデントの主要機能は LuneScript で作成した 外部ツール formatter が担っています。 emacs は、その formatter で取得したインデント量を利用しているだけです。 よって、他の環境でも formatter との連携部を作成すれば、 オートインデント機能を利用できます。

コード補完機能

LuneScript は、クラスフィールドの補完機能を提供しています。

例えば次のようなコードのとき、

// @lnsFront: ok
class Super {
   pub let val:int;
   pri let val2:int { pub };
   pub fn funcsuper():int! {
      return 0;
   }
}
class Test extend Super {
   pub fn __init( val: int ) {
      super( val, val + 1 );
   }
   pub fn func( val: int ):int {
      return 1;
   }
}
let test = new Test( 1 );

test のフィールドの補完を次のように行なえます。

https://ifritjp.github.io/doc/LuneScript/comp1.gif

ここで注目してもらいたいのは次の点です。

  • 継承関係を認識して Super と Test のフィールドが候補にリストされている
  • アクセス制御を認識して Super の private な val2 メンバがリストから除外されている
  • インスタンスからアクセス出来ないコンストラクタ(__init)がリストから除外されている
  • 定義していない get_val2() がリストされている

当たり前の機能ですが、 この当たり前の機能をしっかりとコンパイラ自体が提供しているというのは、 意外に少なかったりします。

カーソル位置の型情報確認

LuneScript は、型推論を対応しています。

これにより、型情報を明示しなくてもコーディングを進められます。

例えば、次のコードには型の明示が一切ありません。

// @lnsFront: ok
foreach val, key in { "abc": 1, "xyz": 10 } {
  print( key, val );
}

これはこれで便利ですが、 逆に型を確認できないというのは欠点とも言えます。

そこで、カーソル位置のシンボルが、どのような型なのかを確認できる機能を提供しています。

確認したいシンボルにカーソルを移動し、 C-c I を実行することで、 そのシンボルの型情報を確認できます。

Syntax エラーチェック機能

LuneScript は、コンパイラなので当然 Syntax エラーチェック機能を持ちます。

このエラーチェック情報の基、エディタ上にエラー箇所を表示可能です。

例えば、次のようなソースで、

// @lnsFront: ok
fn func( val: int ) {
   print( val );
}
let map = { "a": 1, "b":2 };

次の処理を追加するとエラーとなります。

https://ifritjp.github.io/doc/LuneScript/error.gif

https://ifritjp.github.io/doc/LuneScript/error2.PNG

これは、 map 型の item へのアクセス結果は nilable 型になり、 それを func() に与えた場合、 int! と int の型不一致エラーとなります。

このような、ちょっと見過しがちなエラーもエディタ上で簡単に確認できます。

subfile を認識した検索

LuneScript は、大きいモジュールを定義するファイルを 複数のファイルに分割して定義する機能 subfile を持ちます。

この機能を利用することで、ファイルが大きくなってエディタが重くなる、 なんてストレスから開放されます。

しかし、ファイルが分割されてしまうので、 モジュール内の検索性が悪くなる欠点があります。

例えば、あるモジュールが owner.lns, sub1.lns, sub2.lns, sub3.lns で構成されている場合、 あるモジュール内データがどこでアクセスされているかを検索するには、 owner.lns, sub1.lns, sub2.lns, sub3.lns の 4 つのファイルを 切り替えて検索する必要があります。

これは面倒な操作です。

この面倒な操作を、検索時に自動で行ないます。

具体的には owner.lns で検索して見つかない場合は、次は sub1.lns に切り替えて検索。 sub1.lns で見つからない場合は、次は sub2.lns に切り替えて検索。 … この処理を自動で行ないます。

設定

emacs の lns-mode 設定です。

(require 'lns-conf)
;;(require 'lns-flymake)
(require 'lns-flycheck)
(require 'lns-company-mode)
;;(require 'lns-auto-complete)
(require 'lns-helm)

コード補完、Syntax チェックは、環境に合わせてどちらかを選択してください。

  • コード補完

    • flycheck
    • flymake
  • Syntax チェック

    • company-mode
    • auto-complete

LuneScript の flymake, auto-complete 対応は、メンテナンスしていません。 flycheck, company-mode の使用を推奨します。

オートインデントの高速化

github から取得しただけの状態だと、 オートインデント処理はスクリプト版の formatter が動作します。 スクリプト版の formatter は動作が遅いため、 規模の大きいソースファイルでは処理に時間がかかります。

これを高速化するには、Go 版の formatter をビルドします。

Go 版をビルドするには、 github から取得したプロジェクトに対して以下を実行します。

$ cd tools/formatter
$ make build-go

カスタマイズ

次の設定項目で、オートインデントの設定を変更できます。

  • lns-get-formatter-path

    • formatter のパスを返す関数を登録します。
  • lns-proj-info-check-useing-indent-command

    • formatter を使用するかどうかを返す関数を登録します。

プロジェクト

LuneScript は、モジュールパスをプロジェクトからの相対パスで管理します。 このため、emacs にプロジェクトのルートディレクトリを認識させる必要があります。

プロジェクトのルートディレクトリを emacs に認識させるには、 プロジェクトのルートディレクトリに次の lune.js ファイルを作成してください。

lune.js

この lune.js ファイルには、次を書いておきます。

{}

モジュールのパス

次の位置に lune.js を作成した場合、 proj/foo/bar/module1.lsn のモジュールのパスは foo.bar.module1 になります。

proj/lune.js
proj/foo/bar/module1.lsn

最後に

今回の機能は emacs 上で動作確認しています。 ちなみに、コード補完は company-mode, auto-complete, syntax エラーチェックは flymake, flycheck に対応しています。

LuneScript でデフォルトで提供しているのは emacs 向けの設定ですが、 emacs が行なっているのはフロントエンド制御だけで、 バックエンドは全て LuneScript が行なっています。

つまり、 emacs 以外の環境に移植することは十分可能です。 ただ、私自身は emacs ユーザなので、 emacs を最優先で開発しています。

もしも LuneScript に興味をもったなら、 emacs 以外の環境に対応していただけると嬉しいです。