VSCodeの画面をリアルタイムで共有できるサービスを作りました #vsshare

2015-12-21

この記事はVisual Studio / Visual Studio Code Advent Calendar 2015の21日目の記事です。

12月20日担当のたなかさんの記事はこちらです。


いよいよAdvent Calendar最後の週に入りましたね。今年も多くのご参加ありがとうございます!

VSShare β

さて去年の私は、VS Advent Calendarでこんな記事を公開しました。

というわけで、今年は**Visual Studio Codeにも対応した、マルチセッション可能な『リアルタイムコード共有サービス』を作りました。名前はVSShare β』**です。

さらに、この度、VSShare関連のすべてのソースコードをオープンソースとして公開しました。ソースコードはすべてGitHub上で管理しています。リポジトリなどはこちらからどうぞ

言葉だけだとわかりにくいと思いますので、実際に動作させた時のGIF画像を置いておきます。

配信の様子

配信の様子

マルチセッションの様子

マルチセッションの様子

何ができるの

VSShareでできることを簡単にまとめておきます。

今回、実装が間に合わなかったので、VSCode向けの拡張機能のみをリリースしています。Visual Studioとシェル向けの拡張機能のリリースは、もうしばらくお待ちください。

ちなみに、シェル画面の共有にはtmuxのpipe-paneあたりを使って実現しようかな、と考えています。試しに実装してみたものを置いておきます。

また、プロトコルを大きく変更したので、以前まで公開していたVSShareのVisual Studio向け拡張機能とは互換性がありませんので、ご注意ください。

何に使えるの

主に勉強会でのライブコーディングや小規模のペアプロなどで使えると考えています。

今回のリリースでカーソル追従機能を追加したので、『コードがどうなっているか』だけでなく『開発者がどんな操作をしているのか』がより分かりやすくなったかと思います。

またマルチセッションにも対応したので、例えば複数のコードをよく行き来する作業や、複数のユーザーによる同時配信がやりやすくなっています。

ライブコーディングバトルなどのお供にもいかがでしょうか。

どうやって実現してるの

原理は非常に単純で、エディタの拡張機能などを利用して各種データを取得し、SignalRを用いてサーバーと通信を行っています。 これによってサーバー側が受信したものを、別のSignalRのハブを用いてWebクライアントに配信しています。

SignalRは、非同期でクライアントとサーバーとが双方向通信を行うためのライブラリです。 配信の方法として、WebSocketだけでなくロングポーリングなどにも対応していますので、WebSocketに対応していない環境でも利用することができます。

また、去年リリースしたVSShareでは、拡張機能側でシンタックスハイライトなどの装飾情報を取得し、これらの情報をすべて含めてサーバーへ送信していましたが、VSShareβからはテキストと一部の装飾情報のみを送信するように変更しました。この理由は、キーワードに対する装飾情報を含めるとメッセージのサイズが大きくなりすぎて、SignalRでやり取りできるメッセージサイズの問題(maximum message size · Issue #1205 · SignalR/SignalR)にあたってしまうことや、VSCodeでキーワードに対する装飾情報を取得するAPIがないことが関係しています。

その代わり、クライアントサイドにAceを採用して、シンタックスハイライトや高機能な検索を提供しています。この辺りは、今回一緒にプロジェクトを進めてくれた@oboenikuiさんが、かなり頑張って実装してくれました。ありがとうございます。

どうやって使うの

今回はちゃんとドキュメントを用意しました。以下のリンクからどうぞ。

Visual Studio Code向けの拡張機能を作るときのポイント VSShareのVSCode向け拡張機能を作るときに、いくつかハマったポイントがあったのでまとめておきます。参考になれば幸いです。(大したことは書いてないです)

コマンドの定義方法

本家のページにちゃんと書いてあります。Visual Studio Code API Reference

Visual Studio Advent Calendarでも、こちらの記事が非常に良くまとまってて分かりやすいです。

Visual Studio Codeのコマンドパレットから呼び出せるコマンドを定義するには、まずpackage.jsonにコマンド情報を定義します。VSShareはこんな感じです(抜粋)

package.json(抜粋)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
"contributes": {
    "commands": [
      {
        "command": "vsshare.startBroadcast",
        "title": "Start Broadcasting"
      },
      {
        "command": "vsshare.stopBroadcast",
        "title": "Stop Broadcasting"
      },
      {
        "command": "vsshare.restartBroadcast",
        "title": "Restart Broadcasting"
      }
    ]
}

あとは、ソース側でvscodeのcommandsをimportして、registerCommandメソッドを呼び出してあげます。

extension.ts
1
2
3
commands.registerCommand('vsshare.startBroadcast', () => {
        foo();
});

VSShareではEventEmitterを使っていたりしますが、基本的にやっている内容は同じです。

User Settings(コンフィギュレーション)の定義・読み込み方法

コマンドと同様に、package.jsonにコンフィギュレーション情報を定義します。各プロパティ名と、その説明、型、デフォルト値を定義する感じです。VSShareの例を示します。

package.json(一部抜粋)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
"contributes": {
   "configuration": {
      "type": "object",
      "title": "VSShare configuration",
      "properties": {
         "vsshare.hubName": {
            "type": "string",
            "default": "broadcast",
            "description": "Specify the hub name of your VSShare server"
         },
         "vsshare.url": {
            "type": "string",
            "default": "http://vsshare.net/signalr",
            "description": "Specify the url of your VSShare server"
         },
         "vsshare.logLevel": {
            "type": "number",
            "default": 1,
            "description": "[For Server Developer]: Specify log level to show log window.(0: Debug, 1:Info, 2:Warn, 3:Error)"
         }
      }
   }
}

すると、この拡張機能が有効になっているVisual Studio CodeのDefault Settingsには、以下の図のように定義した説明とデフォルト値を含むテンプレートが生成されています。

User Settingsの画面。Default Settingsがカスタマイズされている。

User Settingsの画面。Default Settingsがカスタマイズされている。

あとは、ソース側から定義したコンフィギュレーションを必要なときにロードしてあげます。

extension.ts
1
2
3
4
5
let config = vscode.workspace.getConfiguration('vsshare');
var endpointUrl: string = config['url'] || Controller.DefaultEndpointUrl; // vscode.urlを読み込み
var hubName: string = config['hubName'] || Controller.DefaultHubName; // vscode.hubNameを読み込み
var userName: string = config['userName'];  // vscode.userNameを読み込み

専用の出力パネルを作成する

タスクランナーの出力ウインドウみたいなのが簡単に作れます。

extension.ts
1
2
3
4
let channel = vscode.window.createOutputChannel("foo"); // fooという名前の出力チャンネルを作成
channel.show(); // ウインドウの表示
channel.clear(); // ウインドウのクリア
channel.appendLine("Hello, VSCode!"); // メッセージの追加

終わりに

Visual Studio Code向けの拡張機能、かなり作りやすいです。VSに比べると拡張機能で取得できる情報は限られますが、APIの見通しがよく、今回の拡張機能も簡単に作ることができました。

拡張機能のソースコードはVSShare / Extension-Codeで公開しておりますので、新しく拡張機能を作るときに何かの参考になれば幸いです。最後の方に大分突貫工事をしたので、汚い部分が多々ありますが、時間を見つけてリファクタリングしていこうと思います。

以上、Visual Studio / Visual Studio Code Advent Calendar 2015の21日目の記事でした!是非VSShareをよろしくお願いします!

(記事の質が低くて申し訳ございません!)

このエントリーをはてなブックマークに追加
« 参考文献でWebページを引用するときに便利そうなブックマークレット 『.NET開発テクノロジ入門2016年版Visual Studio 2015対応版』を執筆しました »