読者です 読者をやめる 読者になる 読者になる

SourceChord

C#とXAML好きなプログラマの備忘録。最近はWPF系の話題が中心です。

monaco-editorを使ってみた

JavaScript VSCode

VSCode1.3のリリースと一緒に、monaco-editorのnpmパッケージが公開されました。

てことで、さっそく使ってみました。
実際に動かしてみると、想像以上のクオリティのテキストエディタが一瞬で組み込めてビックリしました!!
今まで、HTMLとJavaScriptテキストエディタ的なの作る時には、aceとか使ってたんですが、今後はmonaco-editorを使おうかな、と思います。

サンプルコード類

以下のリポジトリにサンプルコード類があります。
https://github.com/Microsoft/monaco-editor-samples
二つのテキストの差分表示やら、electronと組み合わせたサンプルなどなど、いろんなサンプルコードがあります。
最初はこの辺を参考にやってみるとよさそう。

あとは、↓のサイトで、各種APIの使い方を、実際にWebページ上で動くサンプルコードで試すことができます。
https://microsoft.github.io/monaco-editor/playground.html

インストール

インストールは以下のコマンドで。

npm install monaco-editor

package.jsonに記録しておきたい場合は、--saveオプションも付けてください。

インストールすると、node_modulesの中にこんな風にダウンロードされます。
f:id:minami_SC:20160720005124p:plain

  • devフォルダ
    • minifyされてないコード一式。
  • minフォルダ
    • minifyされたフォルダ。プロダクション環境ではこちらを使うことを推奨。
  • min-mapsフォルダ
    • minフォルダのファイルに対するソースファイル類

ついでに、このmonaco-editorのpackage.jsonを見てみると、"dependencies": {}という何とも漢らしい記述。

typescriptのコンパイラもそうですが、MSの作るモジュールは、こんな風に依存関係を少なくするように努力してる感があって非常に好感が持てます。

npmのパッケージ依存関係の複雑さは、今年の春先にkik関係のunpublish問題でありったけトラブルになったのが記憶に新しいですよね。

こういう、他に依存せずなんでも自身のパッケージ内に書くっていうのは、npmやJavaScriptのマイクロパッケージ文化とは異なるものかもしれません。
でも、依存関係が複雑すぎて、自分が何使ってるのかわからなくなるようなモジュールより、こういうシンプルなものの方が安心して使えるよなぁ、という気がします。

使い方

ドキュメントのサンプルコードを参考に、以下のようなコードを書いてみます。
変更したのは、monaco-editorのコードを、node_modulesフォルダから直接参照するようにしたくらい。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
</head>
<body>
    <div id="container" style="width:800px;height:600px;border:1px solid grey"></div>
    <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
    <script>
    require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs' }});
    require(['vs/editor/editor.main'], function() {
        var editor = monaco.editor.create(document.getElementById('container'), {
            value: [
                'function x() {',
                '\tconsole.log("Hello world!");',
                '}'
            ].join('\n'),
            language: 'javascript'
        });
    });
</script>
</body>
</html>

こうしてindex.htmlを開いてみると、以下のようにVSCodeで見慣れたようなテキストエディタが領域が表示されます。
f:id:minami_SC:20160720005211p:plain

インテリセンスまでバッチリ効きます。
f:id:minami_SC:20160720005218p:plain

コードの説明

require・・・というコードがところどころ出てきてます。これは、require.jsなどと同じようなAMD形式でのモジュールローダーで、monaco-editor内のloader.jsというファイルで定義されています。
前述の通り、monaco-editorは他のパッケージへの依存を持たず、必要な機能は全部自身のモジュール内で定義しているようです。

↓のコードでは、editor.mainを最初に読みに行き、読み込みが完了したら、第二引数のコールバック関数が実行されます。

    require(['vs/editor/editor.main'], function() {
        var editor = monaco.editor.create(document.getElementById('container'), {
            value: [
                'function x() {',
                '\tconsole.log("Hello world!");',
                '}'
            ].join('\n'),
            language: 'javascript'
        });
    });

このmonaco.editor.create(・・・という部分が肝です。
第一引数にテキストエディタ表示を行いたいDOM要素を指定し、第二引数に各種オプションを指定すればよいみたい。

各種オプションなど

この、monaco.editor.createメソッドを呼び出す際には、以下のようなオプション指定ができます。

        var editor = monaco.editor.create(document.getElementById('container'), {
            value: [
                'function x() {',
                '\tconsole.log("Hello world!");',
                '}'
            ].join('\n'),
            language: 'javascript',
            lineNumbers: true,              // 行数表示の有無
            roundedSelection: false,        // 選択領域の角が丸くなる?
            scrollBeyondLastLine: false,    // 最終行より下までスクロール可能か設定
            readOnly: false,                // 読み取り専用の設定
            theme: "vs-dark"                // テーマの設定
        });

これらのオプションを指定することで、こんな風に色々と設定を変えられるみたい。
f:id:minami_SC:20160720005233p:plain

テキストデータの取得

エディタで編集されたテキストデータは、エディタをのインスタンスに対して、getValue()メソッドを呼び出すことで取得できます。

monaco.editor.create()メソッドの戻り値を変数に格納しておき、ボタン押下時にそのインスタンスのgetValueメソッドを呼び出して、テキストエディタに書かれた文字列を取得してみます。

コード全体は以下の通り。

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
</head>
<body>
    <div id="container" style="width:800px;height:600px;border:1px solid grey"></div>
    <button onclick="showMessage();">ボタン</button>
    <script src="node_modules/monaco-editor/min/vs/loader.js"></script>
    <script>
    var editor;
    require.config({ paths: { 'vs': 'node_modules/monaco-editor/min/vs' }});
    require(['vs/editor/editor.main'], function() {
        editor = monaco.editor.create(document.getElementById('container'), {
            value: [
                'function x() {',
                '\tconsole.log("Hello world!");',
                '}'
            ].join('\n'),
            language: 'javascript'
        });
    });

    function showMessage() {
        var text = editor.getValue();
        alert(text);
    }
</script>
</body>
</html>

f:id:minami_SC:20160720005343p:plain

とりあえず、今回はこの辺まで。