Atomプラグイン plantuml-viewer で大きなUMLを扱った場合の問題と対処法

以前に紹介したAtomプラグインのplantuml-viewerについて。使っていて少々気になる点がありました。

atom.io

概ね問題なく快適に使えているのですが、変換するUMLのサイズが大きくなってくると
極端にレスポンスが悪くなったり、最後の入力結果が反映されなかったりすることがあります。

plantumlによる変換処理は、子プロセスに投げられてバックグラウンドで処理されるのですが、この部分の処理が以下の様になっているために、前述の問題が起きると考えられます。

  • 1文字入力する度に変換処理が走る(その結果、同時に実行されるプロセスが多くなる)
  • 結果が表示されるのは、処理の実行順ではなく完了した順番

シーケンス図で表すとこんな感じでしょうか

http://pierre3net.azurewebsites.net/Content/image/plantuml.svg

実際問題、プレビューを画像をここまで頻繁に更新する必要はないと思うので、「一定時間入力が無かったら更新する」といった、所謂 Throttle の動作にした方が良いのでは。。。 などと考えていたら、Githubにそのものズバリのプルリクエストが上がっていました。

[throttle image updates by KylePDavis · Pull Request #10 · markushedvall/plantuml-viewer · GitHub

これはグッジョブ!と、マージされるのを待っていたのですが一向にマージされる気配が無いので、ひとまず自分の環境だけにでも取り込んでしまいましょう!

plantuml-viewer の画像更新をThrottleにする

修正方法は、プルリクエストの内容を参照して頂く方が早いとは思いますが、ここでもざっくりと説明しておきます。

  • 修正するファイルは1つのみで、以下のパスにあります。
%USERPROFILE%\.atom\packages\plantuml-viewer\lib\plantuml-viewer-view.js
  • このファイルの PlantumlViewerView クラス内に、setTimeout()関数を使用して更新頻度を調整するqueueUpdate()関数を追加します。
function PlantumlViewerView (editor) {

  //...(略)...

  var updateImageTimerId = 0
  function queueUpdate () {
    if (updateImageTimerId) return
    updateImageTimerId = setTimeout(function () {
       updateImage()
       updateImageTimerId = 0
    }, 20)
  }
}
  • setTimeout()の第2引数に、待機時間を指定します。待機時間の間入力が無かった場合に更新処理(updateImage())が実行されます。プルリクエストのコードでは20ミリ秒となっていますが、少し短すぎるような気もします。この辺は実際に動かしながら調節すると良いでしょう。
  • 後は、既存のコードにある 画像更新処理関数updateImage() を作成した queueUpdate() 関数に置き換えるだけです。
// ファイル内のすべてのupdateImage()をqueueUpdate()に置き換える
//...(略)...
function attached () {
  disposables = new CompositeDisposable()
  //updateImage() 置き換え
  queueUpdate()
  if (atom.config.get('plantuml-viewer.liveUpdate')) {
    disposables.add(editor.getBuffer().onDidChange(function () {
      if (loading) {
        waitingToLoad = true
        return
      }
      //updateImage() 置き換え
      queueUpdate()
    }))

    interval = setInterval(function () {
      if (panZoom) {
        if (width !== self.width() || height !== self.height()) {
          //updateImage() 置き換え
          queueUpdate()
            width = self.width()
            height = self.height()
          }
        }
      }, 500)
    }
    //...(略)...

これで、より快適なplantumlライフが送れるようになりました!