JavaScript
Codemirror
caret
cursor
Cattaz

CodeMirrorで他クライアントのカーソル(キャレット)の位置を表示する方法

「CodeMirror」でリアルタイムに同時に、エディタ編集中の他の人(他クライアント)のカーソル(キャレット)の位置を表示する方法を説明します。

本記事の内容を応用すれば、下図のような感じで、他のクライアントのカーソルの位置を様々な色で表示することも可能です。

cattaz_cursor.png

この機能は、富士通研究所のOSSであるMarkdownベースのコラボレーションツール「Cattaz」に実装しています。

Cattazの公式ページ

GitHub - Cattaz

前置き

カーソルを表示する部分に焦点を当て説明するため、以下の説明については省略します。

  • CodeMirrorエディタの入力イベントの取得
  • CodeMirrorエディタ間の通信

方法

CodeMirrorの以下のAPIを利用します。

setBookmark

このAPIを利用することで、自ら生成したDOMノード(マーカー/表示したいデザイン)をエディタ上の指定した位置に挿入できます。

つまり、他クライアントから送られてきたカーソルの位置情報の場所に、何かしらスタイルを装飾したDOMノードを追加できます。

それにより、他クライアントのカーソル位置を表示することができます。

以下に、DOMノードを生成して、そのDOMノードを指定したエディタ上の位置に「setBookmark」でセットするコードの例を示します。

(ES2015(ES6)でのコードの書き方で説明)

// cm:CodeMirrorのインスタンス
// cursorPos:他クライアントから送られてきたカーソルの位置(CodeMirrorにおける {line, ch} のこと)

// DOMノード(マーカー/表示したいデザイン)を生成
const cursorCoords = cm.cursorCoords(cursorPos);
const cursorElement = document.createElement('span');
cursorElement.style.borderLeftStyle = 'solid';
cursorElement.style.borderLeftWidth = '2px';
cursorElement.style.borderLeftColor = '#ff0000';
cursorElement.style.height = `${(cursorCoords.bottom - cursorCoords.top)}px`;
cursorElement.style.padding = 0;
cursorElement.style.zIndex = 0;

// 上記で生成したDOMノードを他クライアントから送られてきたカーソルの位置にセット
// setBookmarkの第1引数:他クライアントから送られてきたカーソルの位置
// 第2引数内のwidget:生成したDOMノード
marker = cm.setBookmark(cursorPos, { widget: cursorElement });

DOMノード生成の部分は、自分の好きなようなスタイルにすると、かっこよくできます。

上記の例は、普通のカーソルのような縦線のデザインです。

setBookmarkで付加したDOMノードは、そのままですとエディタ上の挿入した位置に残り続けるため、他のクライアントからカーソル位置が送られてくる度に、一度以下のようにsetBookmarkのclear()メソッドで挿入したDOMノードを削除し、それから新規にDOMノードを再度挿入すると良いです。

// 挿入したDOMノードのクリア
// marker:setBookmarkのインスタンス
marker.clear();

処理の一連の流れ

処理の大まかな一連の流れの例は以下です。

  1. CodeMirrorエディタの入力イベントからカーソルの位置を取得する
  2. 取得したカーソルの位置を Websocket通信などを用いて他クライアントへ送信する
  3. 他クライアントから送られてきたカーソルの位置を受信したクライアントは、前にsetBookmarkで挿入したDOMノードが存在していたら削除する
  4. 受信したカーソルの位置に、マーカーとなるDOMノードをsetBookmarkで挿入する

また、各クライアントから受信したカーソル位置を各クライアントごとに管理すれば、複数のクライアントのカーソルを同時に表示できるようになります。

まとめ

以上のようにして、CodeMirrorのAPIの「setBookmark」を利用すれば、他クライアントのカーソルの位置を表示できます。

この機能は「Cattaz」にも実装していますので(2018年1月9日現在)、そちらのソースコードも見ると参考になるかもしれません。

GitHub - Cattaz