JeffreyFrancesco.org 公開日 タグ Tag Permalink 現在地 Facebook page Twitter RSS feed

最近テスト用に MovableType を入れて色々弄っておりまして、そんな中でテキストフォーマッタに Markdown を選択してテストエントリを書いてる時に気付いた事など。

オリジナル Markdown(そのコードをベースに PHP Markdown Extra で拡張された機能その他を追加した MultiMarkdown も同様)には「順序リストと順不同リストを並べて書いた時に、2つの別のリストではなく1つのリストとして認識してしまう」という微妙なバグのようなものがあるのを発見しました。

具体的に説明すると、例えば次のようなテキストをフォーマットしようとした時に

*   Foo
*   Bar

1.  Baz
2.  Qux

こちら側としては、次のようなコードに変換される事を期待するのですが(しますよね…?汗)

<ul>
<li>Foo</li>
<li>Bar</li>
</ul>

<ol>
<li>Baz</li>
<li>Qux</li>
</ol>

実際に変換されるコードは、次のようになってしまう訳ですな。

<ul>
<li>Foo</li>
<li><p>Bar</p></li>
<li><p>Baz</p></li>
<li>Qux</li>
</ul>

ただし、実際に使用する上で問題になる事はほぼないと思われます…なぜなら普通の(こういったエントリ的な)文章を書く場合に、こんな感じで続けて別のリストを記述するような場面はまずないからです。汗 何せこのサイトのアップデート時にも今までずっと Markdown 書式で書いたのを MultiMarkdown.pl で HTML 変換してアップしてたのに、今まで全くこれに気付きませんでしたから…冒頭で「微妙なバグのようなもの」と称したのもそのため。

なので、放っておいてもおそらく問題ないかと思います…が、影響なくても不具合を放っておくのはどうしても我慢出来ん!と思われる方もいらっしゃるだろうと思いますので(私の事です、汗)その解決策などを。

一応 PHP Markdown の方にはこのバグ?を回避する為のコードが追加されておりまして、最初そのままオリジナルの方に移植してみたのですが、それだと今度は他のところで不具合が生じたり、納得できる出力が得られなかったりしましたので(というか何か少し冗長というのか、無駄な事してるような気がしないでもない、汗)その辺テストしつつ変更したのが次の部分。_DoLists サブルーチンをほんの少し弄るだけです(強調部分が変更点、オリジナルのコメント部分は削除してあります)。

sub _DoLists {
    my $text = shift;
    my $less_than_tab = $g_tab_width - 1;

    my $marker_ul  = qr/[*+-]/;
    my $marker_ol  = qr/\d+[.]/;
    my $marker_any = qr/(?:$marker_ul|$marker_ol)/;

    foreach my $marker ($marker_ul, $marker_ol) {
        my $whole_list = qr{
            (
              (
                [ ]{0,$less_than_tab}
                (${marker})
                [ \t]+
              )
              (?s:.+?)
              (
                  \z
                |
                  \n            # ←`{2,}`を削除
                  (?=\S)
                  (?!
                    [ \t]*
                    ${marker}[ \t]+
                  )
              )
            )
        }mx;

        if ($g_list_level) {
            $text =~ s{
                    ^
                    $whole_list
                }{
                    my $list = $1;
                    my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";

                    $list =~ s/\n{2,}/\n\n\n/g;
                    my $result = _ProcessListItems($list, $marker_any);

                    $result =~ s{\s+$}{};
                    $result = "<$list_type>" . $result . "</$list_type>\n";
                    $result;
                }egmx;
        }
        else {
            $text =~ s{
                    (?:(?<=\n\n)|\A\n?)
                    $whole_list
                }{
                    my $list = $1;
                    my $list_type = ($3 =~ m/$marker_ul/) ? "ul" : "ol";

                    $list =~ s/\n{2,}/\n\n\n/g;
                    my $result = _ProcessListItems($list, $marker_any);
                    $result = "<$list_type>\n" . $result . "</$list_type>\n\n";
                    $result;
                }egmx;
        }
    }

    return $text;
}

基本的にはオリジナルが ulol もまとめて一気に処理してるのに対し、2回ループして別個に処理をしてるだけです。あとは変数 $whole_list に収められてる正規表現をほんの少し変更したくらい。

考えうる色々なパターンを試してみた限りでは、これで解決してるっぽいです。

…が、おそらく試し切れてないパターンもあるでしょうし、リストの中に他のブロック要素をネストしたりすると思わぬ不具合が発生する可能性も無きにしもあらずですので、一応修正される場合は自己責任でって事でお願いします。先ほど書いた通り、修正しなくても実害はほぼない筈ですので。

という訳で、実は Weblog を更新するのは約2ヶ月ぶりだった。汗