kramdown version: 1.11.1

使生成 head ID 功能支持中文

我查看了 kramdown 选项部分的所有文档,发现它只支持 transliterated_header_ids.
但是我不喜欢将中文音译为拼音,所以干脆直接修改代码好了,还比较简单。

首先找到相关的代码,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
## /lib/kramdown/converter/base.rb

def generate_id(str)
  str = ::Kramdown::Utils::Unidecoder.decode(str) if @options[:transliterated_header_ids]
  gen_id = str.gsub(/^[^a-zA-Z]+/, '')
  gen_id.tr!('^a-zA-Z0-9 -', '')
  gen_id.tr!(' ', '-')
  gen_id.downcase!
  gen_id = 'section' if gen_id.length == 0
  @used_ids ||= {}
  if @used_ids.has_key?(gen_id)
    gen_id += '-' << (@used_ids[gen_id] += 1).to_s
  else
    @used_ids[gen_id] = 0
  end
  @options[:auto_id_prefix] + gen_id
end

我们看上面代码中的 5-8 行,就是对 ID 的转换, str 表示 head 的 raw text.
原代码的意思是:取以字母开始的子串,去掉除了字母,数字,空格,连字符的字符,将空格替换为连字符,将字母变为小写

我们将代码改为如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def generate_id(str)
  str = ::Kramdown::Utils::Unidecoder.decode(str) if @options[:transliterated_header_ids]
  # gen_id = str.gsub(/^[^a-zA-Z]+/, '')
  # gen_id.tr!('^a-zA-Z0-9 -', '')
  # gen_id.tr!(' ', '-')
  # gen_id.downcase!
  # gen_id = 'section' if gen_id.length == 0
  gen_id = str.scan(/([a-zA-Z0-9 \-]|[^\x00-\x7F])/).join
  gen_id.gsub!(/[ \-]+/, '-')
  gen_id.downcase!
  @used_ids ||= {}
  if @used_ids.has_key?(gen_id)
    gen_id += '-' << (@used_ids[gen_id] += 1).to_s
  else
    @used_ids[gen_id] = 0
  end
  @options[:auto_id_prefix] + gen_id
end

代码的意思是:移除 ASCII 字符中除了字母,数字,空格,连字符的字符,但是不移除所有非 ASCII 字符,
然后将其中空格和连字符(包括重复的)转换为一个连字符,再将其转换为小写。

我个人比较喜欢这种表达 ID 的方式,所以就那么改了,你可以随便改成什么样子。

绕过 alt text 中出现 “|” 产生的 bug

先看几个例子体会一下这个 bug 是什么,以下都是链接的例子,当然在图片中也会有问题

  • 出错,会转换为表格。

    1
    [明无梦的博客 | 明无梦](http://www.dreamxu.com/)
    

    转换为:

    1
    2
    3
    4
    5
    6
    7
    8
    <table>
      <tbody>
        <tr>
          <td>[明无梦的博客</td>
          <td>明无梦](http://www.dreamxu.com/)</td>
        </tr>
      </tbody>
    </table>
    
  • 正常,但是需要将 | 转义,显然我不会这样做,我的 Markdown 代码写的没问题,
    没必要修改,而且 ‘|’ 看起来不直观。

    1
    [明无梦的博客 \| 明无梦](http://www.dreamxu.com/)
    

    转换为:

    1
    2
    3
    <p>
      <a href="http://www.dreamxu.com/">明无梦的博客 | 明无梦</a>
    </p>
    
  • 正常,但是链接前后必须有其它文字。为什么我单独写个链接就不行!显然也不是我能接受的解决方案。

    1
    2
    这是一个博客:
    [明无梦的博客 | 明无梦](http://www.dreamxu.com/)
    

    转换为:

    1
    2
    <p>这是一个博客:
    <a href="http://www.dreamxu.com/">明无梦的博客 | 明无梦</a></p>
    

产生这个 bug 的原因很简单,就是 kramdown 支持 PHP Markdown 式的表格,其中有用 | 关键词,
所以会将其转换为表格。

我看 Github 上也有人提出了这个 bug: https://github.com/gettalong/kramdown/issues/135,
看样子是已经修好了。
但是从上面的例子中也可以看出来并没有修好,所以我打算绕过这个 bug, 这是最简单的处理方式了。

我们只要将其 table parse 功能取消掉就好了,反正我也不使用这种表格写法。
注意,如果你用到了这个功能,那么就不要这么做了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# /lib/kramdown/parser/kramdown.rb

def initialize(source, options)
  super

  reset_env

  @alds = {}
  @footnotes = {}
  @link_defs = {}
  update_link_definitions(@options[:link_defs])


  # 原来的代码是这个样子的,将 :table 去掉即可
  #
  # @block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :atx_header,
  #                   :horizontal_rule, :list, :definition_list, :block_html, :setext_header,
  #                   :block_math, :table, :footnote_definition, :link_definition, :abbrev_definition,
  #                   :block_extensions, :eob_marker, :paragraph]
  @block_parsers = [:blank_line, :codeblock, :codeblock_fenced, :blockquote, :atx_header,
                    :horizontal_rule, :list, :definition_list, :block_html, :setext_header,
                    :block_math, :footnote_definition, :link_definition, :abbrev_definition,
                    :block_extensions, :eob_marker, :paragraph]
  @span_parsers =  [:emphasis, :codespan, :autolink, :span_html, :footnote_marker, :link, :smart_quotes, :inline_math,
                   :span_extensions, :html_entity, :typographic_syms, :line_break, :escaped_chars]

end