Emacs から PlantUML を使ってみた

先日、シーケンス図を書くためのツールとして PlantUML というのを教えていただきました。
その時は Eclipse プラグインを使ったんですが、Emacs ユーザの端くれとしては Emacs から使いたいよねってことで使えるようにしてみました。

Emacs と PlantUML の連携方法に関しては一応ここに載っているんですが、リンク先に目を通してすぐ使える人はあまりいないのではないかと思います。

PlantUML のインストール

まずは PlantUML をインストールします。jar ファイルはお好きなところに配置してください。私は /usr/local/jars というディレクトリを作成してそこに配置することにしました。

$ wget http://sourceforge.net/projects/plantuml/files/plantuml.jar/download -O plantuml.jar
$ mkdir /usr/local/jars
$ mv plantuml.jar /usr/local/jars/
$ java -jar /usr/local/jars/plantuml.jar -version
PlantUML version 7937 (Fri Oct 12 02:12:59 JST 2012)
Java(TM) SE Runtime Environment
Java HotSpot(TM) 64-Bit Server VM
1.6.0_35-b10-428-10M3811
Mac OS X

シーケンス図を書くだけであれば不要なんですが、Graphviz も必要みたいです。インストールしていない方はインストールしておくと良いかもしれません。Mac だと Homebrew からインストールできます。

$ brew install graphviz

試しに次の内容ファイルからシーケンス図を作成してみます。

sample.uml

@startuml
Alice -> Bob: Authentication Request
Bob --> Alice: Authentication Response
@enduml

次のコマンドを実行することでカレントディレクトリに sample.png が作成されるはずです。

$ java -jar /usr/local/jars/plantuml.jar sample.uml

sample.png
20121016032130

org-mode でシーケンス図を書いてみる

Emacs 24.1 から ob-plantuml.el が組み込みになっているようなので、Emacs 24.1 以降を使っていればインストール不要です。
古い Emacs を使っている場合はここのコードをコピペして ob-plantuml.el を作成する必要があります。ちなみに Emacs 24.1 以降ではこちらのコードだと所望の結果が得られません1。org-babel-execute:plantuml の返り値を nil にする必要があります。

org-mode の設定として最低限次のような内容を記述しておけば動くと思います。

;; org-plantuml-jar-path は plantuml.jar へのパス
(setq org-plantuml-jar-path "/usr/local/jars/plantuml.jar")
(defun org-mode-init ()
  (org-babel-do-load-languages
   'org-babel-load-languages
   (add-to-list 'org-babel-load-languages '(plantuml . t))))
(add-hook 'org-mode-hook 'org-mode-init)

では早速 Emacs を起動してシーケンス図を書いてみます。Emacs を起動したら M-x org-mode を実行して org-mode にします。
PlantUML のコードを #+BEGIN_SRC plantuml と #+END_SRC の間に記述します。#+BEGIN_SRC plantuml の後には出力ファイル名を :file に、PlantUML の実行時の追加オプションを :cmdline に記述します。コードが書き終わったらコードブロック内で C-c C-c を押して PlantUML を実行します。この辺の説明が意味不明な方は org-babel で調べてください。

例えば次のような内容を UTF-8 で記述した場合は sample2.png が作成されるはずです。
–no-window-system で起動しなければ Emacs 内に画像が表示されるのではないかと思いますが、普段 –no-window-system で使ってるし、そうでない場合は何故か Emacs がすぐに落ちるんで確認できていません・・・

#+BEGIN_SRC plantuml :file sample2.png :cmdline -charset UTF-8
  アリス -> ボブ: Authentication Request
  ボブ --> アリス: Authentication Response
#+END_SRC

sample2.png
20121016035753

org-babel ではコードブロック内で C-c ‘ を押すことで現在記述している言語のメジャーモードで編集することができます。PlantUML 用のメジャーモードとして plantuml-mode も存在するので、色付けなどしてほしい方は導入すると良いかもしれません。

plantuml-mode でシーケンス図を書いてみる

org-mode でもいいんですが、PlantUML だけを使いたいのにいちいち org-mode を使うのもなんかあれですし、何よりも #+BEGIN_SRC とかいうわけのわからないコードが入ると他の人とコードを共有しにくくなるので plantuml-mode をちょっと拡張してみました。

次のコードを init.el に記述することで、拡張子が uml のファイルを plantuml-mode で開いて、C-c C-c を押すことで編集中のファイルに対して PlantUML を実行することになります。適当に書いてるんで、好みに合わせて修正してください。
ちなみに Emacs 内に画像を表示する機能はありません・・・
ちなみにこのモードでは Emacs 内に画像を表示する機能は提供していません・・・

(add-to-list 'auto-mode-alist '("\\.uml\\'" . plantuml-mode))
(autoload 'plantuml-mode "plantuml-mode" "PlantUML mode" t)

(defun plantuml-execute ()
  (interactive)
  (when (buffer-modified-p)
    (map-y-or-n-p "Save this buffer before executing PlantUML?"
                  'save-buffer (list (current-buffer))))
  (let ((code (buffer-string))
        out-file
        cmd)
    (when (string-match "^\\s-*@startuml\\s-+\\(\\S-+\\)\\s*$" code)
      (setq out-file (match-string 1 code)))
    (setq cmd (concat
               "java -jar " plantuml-java-options " "
               (shell-quote-argument plantuml-jar-path) " "
               (and out-file (concat "-t" (file-name-extension out-file))) " "
               plantuml-options " "
               (buffer-file-name)))
    (message cmd)
    (shell-command cmd)
    (message "done")))

(setq plantuml-jar-path "/usr/local/jars/plantuml.jar")
(setq plantuml-java-options "")
(setq plantuml-options "-charset UTF-8")
(setq plantuml-mode-map
      (let ((map (make-sparse-keymap)))
        (define-key map (kbd "C-c C-c") 'plantuml-execute)
        map))

PlantUML の書き方をもっと知りたい!

次の情報源が良さそうです。

以上、Emacs 内に画像を表示できないとあまり意味ない気もしますが Emacs と PlantUML を連携するお話でした!

  1. かと言って Emacs 23 だとそのままで動くかは未確認です