miauのブログ

はてなダイアリー「miauの避難所」をはてなブログに移行しました

ひとりWikiのhttp記法プラグイン作ってみた

前項 の続き。すごく低機能ですが、ようやく出来上がったので置いておきます。


ソースコードは前に貼った gist をそのまま更新してます。

以降は使い方とか苦労した点とか色々です。

使い方

.zip を解凍すると http.dll が入っているので、それを <ひとりWiki のインストールディレクトリ>\plugin 配置してください。ひとり Wiki を立ち上げている場合は再起動が必要です。

記法を「はてな風」にして

[http://〜:title=タイトル]

と書いておくと、

<a href="http://〜">タイトル</a>

のようなリンクに展開されるはずです。

注意点をいくつか書いておきます。

  • http記法はもっと色々できるんですが、とりあえず上記のみ対応しています。
  • マクロと同一行の文字が削れてしまう場合があるようで、これはひとり Wiki 側で修正していただけるらしいです
    • (2011-02-14 追記)2.1.0 で修正していただきましたので、これ以降のバージョンをお使いください
  • まだ信頼できる状態かわからないので、このプラグインを使って作業する場合はこまめにドキュメントを保存してください。
    • 他のツールとの組み合わせがありそうですが、私の環境だとひとりWikiが落ちることも・・・。
(2011-02-14 追記)
[http://www.google.co.jp/:title=Google]

<a href="http://www.google.co.jp/">Google</a>//www.google.co.jp/

のような文字列に展開されるケースがあったようなので、さっそく更新しました。

たぶん原因は C/C++に疲れた人のD言語2.0 - ...ing wiki

Dの文字列はCと違ってゼロ終端ではない
ただし,文字列リテラルはゼロ終端であることが保証されている

と書いてあったのを誤解していて SysAllocString() に "\0" で終端されていない文字列を渡していたせいで。

GetName() なんかでは文字列リテラルをそのまま与えているので問題ないですが、PluginInline() では ~ で結合した結果を返してるので、文字列直後の領域が "\0" になっていることは保証されていないということですね。0.0.2 では文字列末尾に明示的に "\0" を付加する処理を追加してみました。

苦労したところ

文字コードの変換

UTF-8UTF-16UTF-32 あたりの変換は std.utf で行えるんですが、Shift_JIS はこのモジュールでは行えないようで。

追加ライブラリを増やしたくなかったので、

を参考に以下のようにしました。

wstring SjisToUtf16(string s)
{
    if (s is null) {
        return cast(wstring) null;
    }

    wchar[] ws;
    ws.length = s.length + 1;

    auto locale_org = setlocale(LC_CTYPE, "");
    scope(exit) { setlocale(LC_CTYPE, locale_org); }

    auto n = mbstowcs(ws.ptr, s.toStringz(), ws.length);

    return ws[0 .. n].idup;
}

移植性を重視してこのやり方にしたんですが、どうせひとり WikiWindows 上で動作させるわけで、WinAPI 呼び出したほうが楽だった気もしてます。

(2011-02-14 追記)
std.windows.charset.fromMBSz でいけたみたいです。

ひとりWikiプラグインベースとなるモジュールを作ろうとした→挫折

今は http.d に

extern (Windows) {
    BSTR GetName() {
        return SysAllocString("http");
    }
    :
}

みたいな決まりきった記述がたくさんあって、今後 https.d とかを作るときに冗長になってきそうだから、処理を共通化できないかなと思いまして。

最初は htwiki.d みたいなのを作ってそこに外部からパラメータを渡せるようにしようとしたんですけど、テンプレートとかミックスインとか使い方よくわからなくて挫折しました。宣言をパラメータ化するのは難しい気がします。

じゃあ難しいことせずに、htwiki.d のほうで

extern (Windows) {
    BSTR GetName() {
        return SysAllocString(_GetName());
    }
    :
}

と書いておいて、http.d のほうで _GetName() を定義すればいいかなと思ったんですが、モジュール名が http とか httpd とか変わってくるので、それを htwiki.d 側から参照するのが難しそうで、結局あきらめました。

ライブラリの unittest

D 言語の特長の一つに「unittest が言語に組み込まれている」というのがあるんですが、今回のようにライブラリを書いているときはちょっと微妙で。

-unittest オプションをつけてコンパイルしたモジュールを実行した際、main() が無いとエラーになるみたいで、今回はダミーの main() を作って対応しました。今回は小規模なのでいいんですが、もっと大規模になったら他のライブラリと main() が衝突しそうな気がしてます。何かしらいい方法があるとは思うんですけど・・・。

あとアサーション用の関数が assert しかないのがちょっと不便でした。通常のアサーション利用なら問題になりにくいと思うんですけど、開発中は「どこが期待値と違ったのか」がわからないと開発速度が上がらないわけで。assertEquals とかを作って自分で wrap しようかなとも思ったんですけど、

D では
assert() が組み込みの式となっています。コンパイラが assert() に関する知識を持つことで、_assert() 関数は決してreturnしない、 などの情報に基づいた最適化も可能になります。

とのことなので、せっかくなら組み込みで用意してほしい気がしました。

正規表現でのマッチング

これは長くなるので別項目で書こうかと・・・。ちょっとソースに残骸が残ってますけど、今回は正規表現を使わない実装にしました。

今回覚えたこと

.chm ファイル

日本語版のサイトには D 言語マニュアルの .chm ファイルが置いてあるのに気づきました。

気軽に全文検索できるので便利ですね。

問題はまだマニュアルの見方がよくわからないこと。std.conv のマニュアルとか見ても、何ができるのかさっぱり。

D 言語のスタイル

スペースでインデントするとか、ためしに書いたコードをコメントアウトするときは version (none) を使うとか。

IDE(Code::Blocks)

インデントとかちゃんとするなら IDE 入れたほうがいいかなということで、IDE を探したんですが。

というつぶやきを見かけたので Code::Blocks を入れてみました。

インストール方法は

に載っていて、インストール時に Lexer で D を選んでおかないと D 言語で使えません。あと、Toolchain executables で指定するパスは「直下に bin があるディレクトリ」なので、C:\dmd2\windows とかを指定すればよいです。

ちょっと使った感想としては、

  • C++ 製ということもあって、Eclipse 等に比べると軽いのが素敵
  • 日本語は空白文字の表示ができるから最低限はこれで OK
  • プロジェクト内で定義されている関数なんかにはジャンプできるけど、モジュール名や phobos のモジュールへのジャンプができないのはちょっと不便

といったところです。

不便に感じた点

ただの知識不足のような気もしますが、いちおう書いておきます。

  • wstring に対応してない関数が多いような?
    • 例えば std.string.format とか。wstring でやりたい場合どうすればいいんだろ?
    • 一回 UTF-8 に変換したりでできそうだけど・・・それはちょっとやりたくないし
  • char[] から char* への変換は簡単だけど、逆の操作が標準で用意されてない?気がする
  • 多重代入できない?
    • というか配列の各要素をきれいに代入するいい方法がない。
auto split = split(s, ":title=");
auto url = split[0]
auto title = split[1]

みたいな場面で、

auto url, title = split(s, ":title=");

みたいに書く方法ないのかなと。

  • unittest が関数の近所に書けて便利ではあるんだけど、その反面コードの見通しが悪くなってしまっている
    • ほかの関数と unittest の見た目が同じだから、phobos のコードとか読みづらかった
    • 「デフォルトで unittest を折り畳む」設定ができる IDE or エディタがあればいいかも
  • rdmd で実行するとバックグラウンドで起動されて(?)プロンプトの後に標準出力が出てくるので、コマンドが終わったのかわかりにくい
  • 久々に C/C++ 系触るとよく思うんだけど、コンパイル(リンク)時に依存ライブラリを指定するのがちょっと面倒。ソースコード中とかで指定できるとうれしいんだけど・・・そうしてないのは何か理由があるんだろうなぁ。

今後の方向

  • とりあえず正規表現まわりのハマった点をブログに書く
  • たまにしか触らないと忘れてしまうので、プラグインを DLL で書けるソフト(Becky! やら Charu3 とか CLCL とか?)で何かプラグインを書いたりしたい
  • Python と D 言語を組み合わせて処理を高速化している人もいるので、そういうのもやりたい