2015年12月3日木曜日

Marked.js の GFM table の不具合を修正する

右下の空白セルが表示されない

このようなテーブルを書くと右下のセルが表示されません。


|Header 1|Header 2|Header 3|
|--------|--------|--------|
|1       |2       |        |

Marked.js のコードを見ると GFM テーブルは「両側のパイプ無し」と「パイプ有り」に分けて処理されているようです。

参照: marked.js#L93 | GitHub


/**
 * GFM + Tables Block Grammar
 */

block.tables = merge({}, block.gfm, {
  nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
  table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
});

上図テストケースでは table1, table2 が「table: パイプ有り」、 table3 が「nptable: 両側のパイプ無し」にマッチします。

右下のセルが欠落する関係箇所は Lexer のtable (gfm)の部分の 388 行目があやしそうです。
参照: marked.js#L388 | GitHub

Firefox の web コンソールで確認


>> "|1       |2       |        |\n".replace(/(?: *\| *)?\n$/, '').split('\n')
<- Array [ "|1       |2       |" ]

これなら良さそう


>> "|1       |2       |        |\n".replace(/\n$/, '').split('\n')
<- Array [ "|1       |2       |        |" ]

これで table1 の表示ができるようになりましたが、右端のパイプを省略する書式の table2 では効果がありません。 table1 と table2 の両方の書式で動作できるようにと、あれやこれやと正規表現と格闘しながら試してみました。

調べてみると、この不具合は atom/markdown-preview の Issue にもあがっていました。

marked の Pull Request にもパッチが投げられているのを見つけました。

このPull Request #357は 2 Mar, 2014 に投げられたものの未だ取り込まれていないようです。テストしてみると table2 の書式の不具合が改善できませんでした。



押してもだめなら引いてみよう

少し発想を転換して 「右端のパイプが無かったら追加すれば良い!」 作戦を試したところうまくいきました。

どのテーブルも正しく描画できています。

修正箇所

marked.js の 380 行目以降の table (gfm) Lexer を修正します。


  // table (gfm)
    if (top && (cap = this.rules.table.exec(src))) {
      src = src.substring(cap[0].length);

      item = {
        type: 'table',
        header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
        align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
        cells: cap[3].replace(/\n$/, '').split('\n')
      };

      for (i = 0; i < item.align.length; i++) {
        if (/^ *-+: *$/.test(item.align[i])) {
          item.align[i] = 'right';
        } else if (/^ *:-+: *$/.test(item.align[i])) {
          item.align[i] = 'center';
        } else if (/^ *:-+ *$/.test(item.align[i])) {
          item.align[i] = 'left';
        } else {
          item.align[i] = null;
        }
      }

      for (i = 0; i < item.cells.length; i++) {
        if (!item.cells[i].match(/\|$/)) {
          item.cells[i] = item.cells[i] + "\|";
        }
        item.cells[i] = item.cells[i]
          .replace(/^ *\| *| *\| *$/g, '')
          .split(/ *\| */);
      }

      this.tokens.push(item);

      continue;
    }


--- C:/TW/lib/marked.js Fri Jul 31 02:38:48 2015
+++ C:/TW/new/marked.js Thu Dec 03 02:38:34 2015
@@ -388 +388 @@
-        cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
+        cells: cap[3].replace(/\n$/, '').split('\n')
@@ -403,0 +404,3 @@
+        if (!item.cells[i].match(/\|$/)) {
+          item.cells[i] = item.cells[i] + "\|";
+        }

本家にパッチを投げてみました。 Fix a bug in does not display of the table cell if it empty. #697

0 件のコメント :