软件版本
代码高亮问题
在使用jekyll的时候模板一直使用的Liquid,像下面这样的
关于Liquid对于其他标签的解析这里不做深入探讨,只对使用pygements
对代码高亮处理进行说明。如果要使用上述的代码高亮方式,就得每次都要填写同样的代码块,如果要显示行号也要添加对应的linenos标志,否则最终生成的代码块是不带行号的。这里对jekyll自带的代码高亮相关代码(highlight.rb
, 在jekyll-2.5.3/lib/jekyll/tags
目录下)进行分析,:
module Jekyll
module Tags
class HighlightBlock < Liquid::Block
include Liquid::StandardFilters
# The regular expression syntax checker. Start with the language specifier.
# Follow that by zero or more space separated options that take one of three
# forms: name, name=value, or name="<quoted list>"
#
# <quoted list> is a space-separated list of numbers
SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$/
def initialize(tag_name, markup, tokens)
super
if markup.strip =~ SYNTAX
@lang = $1.downcase
@options = {}
if defined?($2) && $2 != ''
# Split along 3 possible forms -- key="<quoted list>", key=value, or key
$2.scan(/(?:\w="[^"]*"|\w=\w|\w)+/) do |opt|
key, value = opt.split('=')
# If a quoted list, convert to array
if value && value.include?("\"")
value.gsub!(/"/, "")
value = value.split
end
@options[key.to_sym] = value || true
end
end
@options[:linenos] = "inline" if @options.key?(:linenos) and @options[:linenos] == true
#@options[:linenos] = "inline"
#@options[:lineanchors] = true
else
raise SyntaxError.new <<-eos
Syntax Error in tag 'highlight' while parsing the following markup:
#{markup}
Valid syntax: highlight <lang> [linenos]
eos
end
end
def render(context)
prefix = context["highlighter_prefix"] || ""
suffix = context["highlighter_suffix"] || ""
code = super.to_s.strip
is_safe = !!context.registers[:site].safe
output =
case context.registers[:site].highlighter
when 'pygments'
render_pygments(code, is_safe)
when 'rouge'
render_rouge(code)
else
render_codehighlighter(code)
end
rendered_output = add_code_tag(output)
prefix + rendered_output + suffix
end
def sanitized_opts(opts, is_safe)
if is_safe
Hash[[
[:startinline, opts.fetch(:startinline, nil)],
[:hl_linenos, opts.fetch(:hl_linenos, nil)],
[:linenos, opts.fetch(:linenos, nil)],
[:encoding, opts.fetch(:encoding, 'utf-8')],
[:cssclass, opts.fetch(:cssclass, nil)]
].reject {|f| f.last.nil? }]
else
opts
end
end
def render_pygments(code, is_safe)
require 'pygments'
@options[:encoding] = 'utf-8'
highlighted_code = Pygments.highlight(
code,
:lexer => @lang,
:options => sanitized_opts(@options, is_safe)
)
if highlighted_code.nil?
Jekyll.logger.error "There was an error highlighting your code:"
puts
Jekyll.logger.error code
puts
Jekyll.logger.error "While attempting to convert the above code, Pygments.rb" +
" returned an unacceptable value."
Jekyll.logger.error "This is usually a timeout problem solved by running `jekyll build` again."
raise ArgumentError.new("Pygments.rb returned an unacceptable value when attempting to highlight some code.")
end
highlighted_code
end
def render_rouge(code)
require 'rouge'
formatter = Rouge::Formatters::HTML.new(line_numbers: @options[:linenos], wrap: false)
lexer = Rouge::Lexer.find_fancy(@lang, code) || Rouge::Lexers::PlainText
code = formatter.format(lexer.lex(code))
"<div class=\"highlight\"><pre>#{code}</pre></div>"
end
def render_codehighlighter(code)
"<div class=\"highlight\"><pre>#{h(code).strip}</pre></div>"
end
def add_code_tag(code)
# Add nested <code> tags to code blocks
code = code.sub(/<pre>\n*/,'<pre><code class="language-' + @lang.to_s.gsub("+", "-") + '" data-lang="' + @lang.to_s + '">')
code = code.sub(/\n*<\/pre>/,"</code></pre>")
code.strip
end
end
end
end
Liquid::Template.register_tag('highlight', Jekyll::Tags::HighlightBlock)
在解析md时对于代码段会进行是否显示行号的判断@options[:linenos] = "inline" if @options.key?(:linenos) and @options[:linenos] == true
并且默认使用inline的格式,就是行号与代码在一起,如果要复制代码,行号也会一起被选中复制。我们可以根据这个判断条件,直接自动添加显示行号的标记,我们这里不使用inline,使用lineanchors,让行号与代码分开。
#@options[:linenos] = "inline" if @options.key?(:linenos) and @options[:linenos] == true
@options[:lineanchors] = true
直接注释掉原来判断行号显示的代码,添加上面的代码,我们就会让jekyll在解析代码块的时候自动添加显示行号。如果要使用上面的方法,需要个性jekyll的lib,这样不是很通用。可以复制上面的highlight.rb
到博客的_plugins
目录下,修改highlight.rb
如下所示:
module Jekyll
module Tags
class CustomeHighlightBlock < Liquid::Block #因为jekyll的lib里有相同类的定义,这里修改为其他类名
include Liquid::StandardFilters
# The regular expression syntax checker. Start with the language specifier.
# Follow that by zero or more space separated options that take one of three
# forms: name, name=value, or name="<quoted list>"
#
# <quoted list> is a space-separated list of numbers
SYNTAX = /^([a-zA-Z0-9.+#-]+)((\s+\w+(=(\w+|"([0-9]+\s)*[0-9]+"))?)*)$/
def initialize(tag_name, markup, tokens)
super
if markup.strip =~ SYNTAX
@lang = $1.downcase
@options = {}
if defined?($2) && $2 != ''
# Split along 3 possible forms -- key="<quoted list>", key=value, or key
$2.scan(/(?:\w="[^"]*"|\w=\w|\w)+/) do |opt|
key, value = opt.split('=')
# If a quoted list, convert to array
if value && value.include?("\"")
value.gsub!(/"/, "")
value = value.split
end
@options[key.to_sym] = value || true
end
end
@options[:lineanchors] = true # 要修改的地方
else
raise SyntaxError.new <<-eos
Syntax Error in tag 'highlight' while parsing the following markup:
#{markup}
Valid syntax: highlight <lang> [linenos]
eos
end
end
end
end
end
Liquid::Template.register_tag('highlight', Jekyll::Tags::CustomeHighlightBlock) #因为jekyll的lib里有相同类的定义,这里修改为其他类名
根据上面的代码进行相应修改即可。剩下的就是要个性CSS样式了,可以根据这个来修改,本文中的代码样式就是根据该链接进行修改的。
我在配置的时候使用的是kramdown
来解析markdown文件的,就是像github那样的使用~~~ lang ~~~
的代码块格式,这里可以通过使用一个开源的插件kramdown-with-pygments来支持代码高亮,添加了该插件后还要进行一定的修改,因为该插件里有自己对代码块的解析及输出最终的html,这个与前面我们自己修改的highlight.rb有些许冲突。因为highlight.rb
是针对Liquid里的标签tag进行识别处理的,这里的是直接进行converter处理,修改如下:
def convert_codeblock(el, indent)
attr = el.attr.dup
lang = extract_code_language!(attr) || @options[:kramdown_default_lang]
code = pygmentize(el.value, lang)
# code_attr = {}
# code_attr['class'] = "language-#{lang}" if lang
# "#{' '*indent}<div class=\"highlight\"><pre#{html_attributes(attr)}><code#{html_attributes(code_attr)}>#{code}</code></pre></div>\n"
code = code.sub(/<pre>\n*/,'<pre><code class="language-' + lang + '" data-lang="' + lang + '">')
code = code.sub(/\n*<\/pre>/,"</code></pre>")
code.strip
end
def convert_codespan(el, indent)
attr = el.attr.dup
lang = extract_code_language!(attr) || @options[:kramdown_default_lang]
code = pygmentize(el.value, lang)
# if lang
# attr['class'] = "highlight"
# if attr.has_key?('class')
# attr['class'] += " language-#{lang}"
# else
# attr['class'] = "language-#{lang}"
# end
# end
# "<code#{html_attributes(attr)}>#{code}</code>"
"<code>#{code}</code>"
end
经过上面的处理,两种代码高亮方式就都支持了。说了这么多,要解决的问题就是如何使用pygments
对代码进行高亮显示,并默认添加行号。通过这次的修改过程,对jekyll的一些代码高亮处理的逻辑有所了解了,但是jekyll依赖的其他的库还不了解。同时也勾起了对ruby的学习兴趣,如今使用ruby开发的各种便利工具有很多,如cocoapods
,jekyll
等,学习下ruby还是有很多好处的。
在创建新的文章时title是中文时自动转拼音问题
在创建新的文章时,要填写title,这个title也会在文章内填写好,jekyll默认不支持非ascii码的title,这里使用了hz2py
这个gem, 在Rakefile
里对 post task进行相应修改。 这样就可以直接在title里填写中文了。
task :post do
abort("rake aborted: '#{CONFIG['posts']}' directory not found.") unless FileTest.directory?(CONFIG['posts'])
title = ENV["title"] || "new-post"
# 新增用来接收category和description参数
category = ENV["category"] || ""
description = ENV["description"] || ""
tags = ENV["tags"] || "[]"
# 新增用来将汉字转换成拼音,因为url好像不支持中文。当然在文件顶部require了Hz2py
slug = Hz2py.do(title, :join_with => '-', :to_simplified => true)
...
end # task :post
附上解决问题过程中遇到的关于jekyll的好资源供大家学习:
- jekyll官网, jekyll官网中文
- Jekyll QuickStart
- 中文title转拼音
- kramdown-with-pygments
- jekyll主题 这里有很多,可以自己选择中意的