ujimushi(@旧sradjp(15364))の日記

旧スラドの日記の引越先です

plantumlのstdlib(Google Material Design)を利用する環境をEmacsで作る

はじめに

Emacsを使って自分が考えていることを図で表現することについて, plantumlを利用できないかと考えています。

ネットでplantumlに関する日本語情報を検索すると, stdlibに関しては,ネットワーク構成図を描くような情報ばかりで, 汎用的な図の描画に利用する方法についてはあまり情報がないような気がします。

そこで,色々なアイコンが利用可能なGoogle Material Design を利用できないかと色々調べてみました。

Material Designのstdlib

stdlibのgithubレポジトリはplantuml-stdlibです。

Material Designのアイコンはバージョン2系バージョン7系があるようなのですが, バージョン7系について自分が利用しているEmacsで利用する方法について試してみました。

アイコンの検索は次のサイトが有効かと思います。 Material Design

Emacsのplantuml-modeの設定

Emacsでplantumlを利用するには,plantuml-modeをインストールするのがいいかと思います。

インストールする方法は省略します。 下の設定は,javaが実行できる(PATHが通っているところにjavaコマンドがある)前提での設定です。 init.el内相当のところに記入する感じです。

(require 'plantuml-mode)
(setq plantuml-jar-path "/home/ujimushi/.local/opt/plantuml.jar")
(setq plantuml-output-type "png")
(setq plantuml-default-exec-mode 'jar)
(setq plantuml-indent-level 2)
(setenv "PLANTUML_LIMIT_SIZE" "8192")

Emacsのauto-completeの設定

今ではもう化石と化しているEmacs補完パッケージauto-completeですが, plantumlのようにわりと単純な補完だけでいいケースでは, 自分で制御しやすいauto-completeもわりと使い勝手がいい面もあります。

今回は今さらですが,auto-completeで編集環境を構築してみます。

auto-completeの辞書登録機能

元々,plantuml-modecapfcompany-modeでのキーワード補完が可能なのですが, auto-completecapf対応がそれほど良くないので, 個別に辞書に登録することにします。

Emacsauto-completeでは,ac-dictionary-directoriesで設定されているフォルダに モード名のファイルを作成してキーワード辞書を登録しておくと, そのモード名に登録したキーワードを補完するようになります。

plantumlのキーワードの登録

Emacs上で*scratch*バッファ上で, (print plantuml-kwdList) と入力してctrl+jとすると,plantumlのキーワードのリストが表示されます。 2つ目の(とそれに対になる)の間をテキストファイルに保存します。

その後,そのテキストファイルに対して,Julia言語で次のような処理をして, 一行ーキーワードのテキストファイルを作成します。

open("plantuml-dict-example.txt", "w") do io
    kwds = map(
        replace(read("kwdlist.txt", String), " t " => "\n") |>
            IOBuffer |> readlines
    ) do x
        strip(x, ['\"'])
    end
    for s in kwds
        println(io, "$s")
    end
end

Material Designのインクルードファイルの補完の工夫

auto-completeだと直接Material Designのinclude補完は難しいので, githubのmaterial7.4.47のフォルダ構造と同じ構造のダミーファイルを作成, そのフォルダを自分がplantumlのファイルを置いているフォルダ内に シンボリックリンクを作成して,auto-completeのファイルパスの補完機能を使って 補完させようと考えました。

using JSON

function make_plantuml_stdlib_materail7_dummy_files(srcdir, destdir)
    categories = open(joinpath(srcdir, "all.json"), "r") do io
        JSON.parse(io)
    end
    isdir(destdir) || mkdir(destdir)

    mydict = Dict()

    for category in categories
        files = readdir(joinpath(srcdir, category))
        mydict[category] = map(x -> first(splitext(x)), filter(x -> endswith(x, ".puml"), files))
    end

    for (key, values) in mydict
        module_path = joinpath(destdir, key)
        isdir(module_path) || mkdir(module_path)
        for v in values
            touch(joinpath(module_path, v))
        end
    end
    nothing
end

の後, plantuml-stdlibのレポジトリからソースをダウンロードして展開してから,

rootfolder = "/home/ujimushi/Downloads/plantuml-stdlib-master/stdlib/material7.4.47"
destfolder = "material7.4.47"
make_plantuml_stdlib_materail7_dummy_files(rootfolder, destfolder)

ぐらいでダミーのファイルを作成してから,作業フォルダ内から生成したダミーファイルを格納しているフォルダにシンボリックリンクを貼るといいでしょう。

Material Designのスプライトキーワードの登録

最後に,スプライトのキーワードの登録を考えてみます。先ほどplantumlのstdlibのgithubアーカイブのフォルダ 構成から生成をしてみます。次のような関数を作成します。

using JSON
function write_stdlib_mdi7_kwdfile(srcdir, destfile)
    categories = open(joinpath(srcdir, "all.json"), "r") do io
        JSON.parse(io)
    end
    tmpdict = Dict()

    for category in categories
        files = readdir(joinpath(rootfolder, category))
        tmpdict[category] = map(x -> first(splitext(x)), filter(x -> endswith(x, ".puml"), files))
    end
    open(destfile, "w") do io
        for (_, values) in tmpdict
            for v in values
                if v != "all"
                    println(io, "<\$mdi$(uppercasefirst(v))>")
                end
            end
        end
    end
end

から,

rootfolder = "/home/ujimushi/Downloads/plantuml-stdlib-master/stdlib/material7.4.47"
destfile = "plantuml-icon-keywords.txt"
make_plantuml_stdlib_materail7_dummy_files(rootfolder, destfolder)

を実行すると,できたテキストファイルの中身は次のような感じです。

<$mdiTooltipAccount>
<$mdiTooltipCellphone>
<$mdiTooltipCheck>
<$mdiTooltipCheckOutline>
<$mdiTooltipEdit>
<$mdiTooltipEditOutline>
<$mdiTooltipImage>
<$mdiTooltipImageOutline>
<$mdiTooltipMinus>
<$mdiTooltipMinusOutline>
<$mdiTooltipOutline>
<$mdiTooltipPlus>
...

これを先ほどのkwdlist.txtと結合して,plantuml-modeという拡張子なしのテキストファイルを用意しておきます。

auto-completeの設定

最後にauto-completeの設定をします。

(require 'auto-complete-config)
;; 自分で登録した辞書を置くディレクトリ
(add-to-list 'ac-dictionary-directories "~/.emacs.d/ac-dict")

(ac-config-default)

;; Emacs 30用のワークアラウンド
(add-hook 'auto-complete-mode-hook
          (lambda ()
            (setq ac-sources (remove 'ac-source-abbrev ac-sources))))

;; 文字列内でも補完をする設定
(setq ac-disable-faces nil)

;; plantuml用の補完候補の登録
(add-hook 'plantuml-mode-hook
      (function (lambda ()
              (setq ac-sources
                '(ac-source-dictionary ac-source-filename ac-source-words-in-same-mode-buffers)))))

;; auto-completeをplantuml-modeで自動で有効にする設定
(add-to-list 'ac-modes 'plantuml-mode)

先ほど作成したplantuml-modeファイルを~/.emacs.d/ac-dictフォルダの中に保存します。

そして,作業用フォルダ内に先ほど作成したダミーファイル群のフォルダをターゲットに シンボリックリンクを作成すると環境作成は完了です。

使用例とか

基本的にはplantumlのスプライトの利用方法を見ていただければと思います。 後は,配置図とかのヘルプが参考になるでしょうか。

ちょっとした利用例です。 !includeのところは個別にincludeするとコンパイルの時間も短くてすむかと思います。

@startuml
!include <material7/all>

card 自宅 [
自宅
<$mdiHome,color=orange>
]
card 会社 [
会社
<$mdiOfficeBuilding,color=blue>
]
card 駅1 [
家の最寄り駅
<$mdiTrain,color=red>
]
card 駅2 [
会社の最寄り駅
<$mdiTrain,color=green>
]

自宅 -> 駅1 : <$mdiBike,scale=0.4>自転車
駅1 -> 駅2 : <$mdiTrain,scale=0.4>電車
駅2 -> 会社 : <$mdiWalk,scale=0.4>徒歩

@enduml

まとめ

とりあえずEmacsでplantumlを利用しやすいように環境を構築してみました。 といっても,普通はcompany-mode等でセットアップすると思うので, 非常に古めかしい環境での構築となりました。(単に日本語の補完パッケージとの兼ね合いです。)

ただ,図を描くのはまだまだ勉強中でこれからといった感じです。