Last active
January 16, 2016 17:31
-
-
Save KitaitiMakoto/0779a34fd74bae96468f to your computer and use it in GitHub Desktop.
Gihyo Digital Publishingの本のソースコードをシンタックスハイライトする
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# coding: utf-8 | |
gem 'epub-parser', '>= 0.2.4' | |
gem 'epub-maker', '0.0.3' | |
require 'English' | |
require 'epub/maker' | |
require 'rouge' | |
require 'rouge/lexers/docker' | |
ROUGE_THEME = 'github' | |
ROUGE_CSS_SCOPE = 'code' | |
CSS_PATH = 'syntax.css' | |
$formatter = Rouge::Formatters::HTML.new(wrap: false) | |
$default_lexers = { | |
'.rb' => :Ruby, | |
'Vagrantfile' => :Ruby, | |
'.yaml' => :YAML, | |
'.yml' => :YAML, | |
'.toml' => :TOML, | |
'.sh' => :Shell, | |
'.go' => :Go, | |
'.json' => :JSON, | |
'nginx.conf' => :Nginx, | |
'Dockerfile' => :Docker, | |
'.ini' => :INI | |
} | |
$lexers = { | |
'urn:uuid:bffeb831-ee98-40a9-9eae-b5693185ce74' => { | |
'リスト2 パッケージからインストールした場合の設定ファイル' => :TOML, | |
'リスト6 Private Variableの参照' => :YAML, | |
'リスト11 envの設定' => :YAML, | |
'リスト12 scriptの記述例' => :YAML, | |
'リスト13 notifyの設定例' => :YAML, | |
'リスト14 publishの設定例' => :YAML, | |
'リスト15 gitの設定例' => :YAML, | |
'リスト16 sshの設定例' => :YAML, | |
'リスト17 bashの設定例' => :YAML, | |
'supervisord.conf' => :INI | |
}, | |
'urn:uuid:14eadcd9-214a-4f45-9623-d8ac54f22af7' => { | |
'Ruby' => :Ruby, | |
'C言語' => :C, | |
'Perl' => :Perl, | |
'Python' => :Python, | |
'ノンブロッキングI/Oはfdのフラグ' => :Ruby, | |
'システムコール' => :C, | |
'fdがオープンされたまま残る' => :Python, | |
'timegm関数と閏秒' => :C, | |
'Rubyにおける時刻の扱いの問題' => :PlainText, | |
} | |
} | |
def main(argv) | |
book_path = argv.shift | |
$book = parse_book(book_path) | |
add_css_files | |
highlight_contents | |
update_or_insert_modified_date Time.now | |
rescue => error | |
$stderr.puts error | |
$stderr.puts error.backtrace | |
$stderr.puts "Usage: ruby #{$PROGRAM_NAME} EPUB_PATH" | |
abort | |
end | |
def parse_book(path) | |
EPUB::Parser.parse(path) | |
end | |
def add_css_files | |
dummy_origin = Addressable::URI.parse('file:///') | |
rootfile_path = dummy_origin + $book.ocf.container.rootfile.full_path | |
theme = ROUGE_THEME | |
scope = ROUGE_CSS_SCOPE | |
if $book.unique_identifier.content == 'urn:uuid:479892c1-6cd6-409e-915e-647aca7e25a2' | |
scope = 'code[data-rouge-language="nginx"]' | |
end | |
style = Rouge::Theme.find(theme).new(scope: scope).render | |
$book.package.edit do |package| | |
package.manifest.make_item do |item| | |
item.href = Addressable::URI.parse("../#{CSS_PATH}") # IMPROVEMENT: Less need to call Addressable::URI.parse explicitly | |
# IMPROVEMENT: Want to call item.entry_name = $css_path | |
item.media_type = 'text/css' | |
item.id = CSS_PATH | |
item.content = style | |
item.save # IMPROVEMENT: Less need to call save explicitly | |
end | |
end | |
end | |
def highlight_contents | |
$book.resources.select(&:xhtml?).each do |xhtml| | |
xhtml.edit_with_nokogiri do |doc| | |
add_links_to_css_files(doc, xhtml) | |
markup(doc) | |
end | |
end | |
end | |
def add_links_to_css_files(doc, xhtml) | |
dummy_root_iri = Addressable::URI.parse('http://example.net/') | |
xhtml_entry_name = dummy_root_iri + xhtml.entry_name | |
head = (doc/'head').first | |
entry_name = dummy_root_iri + CSS_PATH | |
href = entry_name.route_from(xhtml_entry_name) | |
link = Nokogiri::XML::Node.new('link', doc) | |
link['href'] = href | |
link['type'] = 'text/css' | |
link['rel'] = 'stylesheet' | |
head << link | |
end | |
def markup(doc) | |
lexers = $default_lexers.merge($lexers[$book.unique_identifier.content] || {}) | |
lexers_regexp = Regexp.new('(?<extension>' + lexers.keys.map {|k| k.gsub('.', '\.')}.join('|') + ')') | |
(doc/'code').each do |code| | |
list_name = '' | |
case $book.unique_identifier.content | |
when 'urn:uuid:bffeb831-ee98-40a9-9eae-b5693185ce74' | |
list_name = code.parent.previous_element.content if code.parent.previous_element | |
when 'urn:uuid:14eadcd9-214a-4f45-9623-d8ac54f22af7' | |
classes = code['class'] || '' | |
classes = classes.split(/\s+/) | |
next if classes.include? 'bw' | |
prev = code.parent.previous_element | |
while prev | |
if prev.name =~ /h\d/ | |
list_name = prev.content | |
break | |
end | |
prev = prev.previous_element | |
end | |
if list_name == 'クローズして問題を解決する' | |
list_name = if code.content.start_with? 'from ' | |
'Python' | |
elsif code.content.start_with? 'use ' | |
'Perl' | |
end | |
end | |
unless list_name.match lexers_regexp | |
list_name = case list_name | |
when '' | |
'C言語' | |
else | |
'Ruby' | |
end | |
end | |
when 'urn:uuid:479892c1-6cd6-409e-915e-647aca7e25a2' | |
code_title_element = code.parent.previous_element | |
classes = [] | |
if code_title_element && code_title_element['class'] | |
classes = code_title_element['class'].split(/\s+/) | |
end | |
if classes.include? 'code_title' | |
list_name = code_title_element.content | |
end | |
if list_name.match 'nginxの公式リポジトリ情報(Debian GNU/Linux)' | |
list_name = '' | |
end | |
if list_name.match 'nginxの公式リポジトリ情報(Debian GNU/Linux)' | |
list_name = '.ini' | |
end | |
content = code.content | |
unless content.lines.first.chomp == '# pkg install nginx' or content[0] == '$' | |
list_name = 'nginx.conf' | |
end | |
end | |
match = list_name.match lexers_regexp | |
list_marker = '◆リスト' | |
if match and | |
($book.unique_identifier.content == 'urn:uuid:bffeb831-ee98-40a9-9eae-b5693185ce74' ? list_name.start_with?(list_marker) : true) | |
lexer_name = lexers[match[:extension]] | |
lexer = Rouge::Lexers.const_get(lexer_name).new | |
if $book.unique_identifier.content == 'urn:uuid:479892c1-6cd6-409e-915e-647aca7e25a2' | |
code['data-rouge-language'] = 'nginx' | |
end | |
else | |
lexer = Rouge::Lexer.guess(source: code.content) | |
end | |
code.inner_html = $formatter.format(lexer.lex(code.content)) | |
end | |
end | |
def update_or_insert_modified_date(time) | |
$book.package.edit do |package| | |
modified = package.metadata.metas.find {|meta| | |
meta.property == 'dcterms:modified' | |
} | |
unless modified | |
modified = EPUB::Publication::Package::Metadata::Meta.new | |
modified.property = 'dcterms:modified' | |
end | |
modified.content = time.utc.iso8601 | |
end | |
end | |
main(ARGV) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment