-
-
Save ergoz/8453858 to your computer and use it in GitHub Desktop.
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
#!/usr/bin/env ruby | |
#encoding: utf-8 | |
# Внимание! Скрипт работает только на версии ruby 2.0 | |
# create_rus_ruby_book filename -> возвращается результат обработки файла; | |
# create_rus_ruby_book dirname -> все *.md файлы будут обработаны. Результат сохраняется в *.tex файл с тем же именем (файл будет перезаписан). | |
require 'redcarpet' | |
module Redcarpet; module Render; | |
class RusRubyBook < Base | |
# Блочные элементы. | |
def normal_text(text) | |
text = escape(text) | |
end | |
def paragraph(text) | |
"#{text}\n\n" | |
end | |
# Вложенный нумерованный список от маркированного пустой строкой не отделяется. | |
def list( content, list_type ) | |
case list_type | |
when :ordered | |
"\\begin{enumerate}\n" + | |
"#{content}" + | |
"\\end{enumerate}\n\n" | |
when :unordered | |
"\\begin{itemize}\n" + | |
"#{content}" + | |
"\\end{itemize}\n\n" | |
end | |
end | |
def list_item( content, list_type ) | |
"\\item #{content}" | |
end | |
def block_code( code, language ) | |
case language | |
when 'ruby' | |
"\\begin{verbatim}\n" + | |
code.gsub(/^ /, '') + | |
"\\end{verbatim}\n\n" | |
when 'longtable' | |
code = code.lines | |
code[0] = code[0] + '\hline' | |
code[1...code.size].map! { |line| line << '\\\\ \hline' } | |
code = code.join("\n").gsub( '~', '\textasciitilde\-, ' ) | |
"\\begin{longtable}" + | |
"#{code}\n" + | |
"\\end{longtable}\n\n" | |
# when "table" | |
# "\\begin{tabular}" + | |
# "#{code}" + | |
# "\\end{tabular}\n\n" | |
when "note" | |
"\\begin{note}" + | |
"#{code}" + | |
"\\end{note}\n\n" | |
else | |
"\\begin{verbatim}\n" + | |
"#{code}" + | |
"\\end{verbatim}\n\n" | |
end | |
end | |
# > Это сказал Дональд. | |
# >: Дональд | |
def block_quote(quote) | |
author = quote.match( /^:\s+(.+)$/ ) | |
( quote = author.pre_match ) if author | |
"\\epigraph{\n#{quote.rstrip}\n}" + "{#{author && author[1]}}" + "\n\n" | |
end | |
# Конец списка ключей. | |
def hrule | |
"\\end{keylist}\n\n" | |
end | |
# Строчные элементы. | |
def link( link, title, content ) | |
case content | |
# [](link) - создание метки. | |
when nil | |
"\\hypertarget{#{link}}{}" | |
else # обработка внутренних ссылок. | |
"\\hyperlink{#{link}}{\\textcolor{grey}{#{content}}}" | |
end | |
end | |
# <link> | |
def autolink(link, link_type) | |
"\\href{#{link}}{#{link}}" | |
end | |
def header( title, level ) | |
case level | |
# # Заголовок главы. | |
when 1 | |
"\\chapter{#{title}}\n\n" | |
# ## Заголовок подглавы. | |
when 2 | |
"\\section{#{title}}\n\n" | |
# ### Заголовок подподглавы | |
when 3 | |
"\\subsection{#{title}}\n\n" | |
# #### Заголовок пункта | |
when 4 | |
"\\subsubsection*{#{title}}\n\n" | |
# ##### Начало списка ключей: | |
when 5 | |
"\\begin{keylist}{#{title}}\n\n" | |
# ###### Небольшой резервный заголовок | |
when 6 | |
"\\paragraph{#{title}}\\-,\\\\\n\n" | |
end | |
end | |
def codespan(code) | |
# Declare method hack. | |
# `.method(params) {block} # -> return` | |
def_method = /^ | |
(?<name> [\.:]{1,2} [\w\%\/\~\^\[\]\`\s\&\|,+=\-\*<>]* [=!\?]?) | |
(?<params> \(.+\))? | |
(?<block> \s \{.+?\})? | |
(?<ret> \s \#\s->\s .+)? | |
$/x | |
return code.gsub(def_method) do |match| | |
name = escape Regexp.last_match(:name) | |
params = escape_method( Regexp.last_match(:params) ) | |
block = escape_method( Regexp.last_match(:block) ) | |
ret = escape_method( Regexp.last_match(:ret) ) | |
return "\\verb`#{name}`" unless params || block || ret | |
"\\declare{#{name}#{params}#{block}}{#{ret}}" | |
end if code =~ def_method | |
# Declare math hack. | |
# `$123 * 10^{-10}$` | |
return code if code =~ /\A\$.+?\$\z/ | |
# verbatim: `text` | |
if code.include? '`' | |
"\\verb\'#{code}\'" | |
else | |
"\\verb\`#{code}\`" | |
end | |
end | |
# _text_ or *text* | |
def emphasis(text) | |
"\\italy{#{text}}" | |
end | |
# __text__ or **text** | |
def double_emphasis(text) | |
"\\textbf{#{text}}" | |
end | |
# Если вы хотите закончить строку, а не параграф, | |
# то в конце строки ставится два пробела. | |
def linebreak | |
"\n\\\\*" | |
end | |
# Предварительная и завершающая обработка. | |
# `key` в начале параграфа считается элементом списка ключей. | |
def preprocess(full_document) | |
keylist = / ^\#{5,5}\s.+?$ | |
.+? | |
\*{5,5} | |
/mx | |
full_document.gsub(keylist) do |list| | |
list.gsub(/\n\n(`{1,2})([^`].*?)\1/) do |key| | |
match = $2.gsub( /\\(\S)/, '\textbackslash \1' ) | |
.gsub( /--/, '\twominus ' ) | |
.gsub( '{', '\\\\\\{') | |
.gsub( '}', '\\\\\\}') | |
"\n\n" + "\\key{#{match}}" | |
end | |
end | |
end | |
# def block_html(html) | |
# html | |
# end | |
# def raw_html(html) | |
# html | |
# end | |
private | |
# Fix: вместо нескольких проходов .gsub, использовать один .map. | |
def escape(text) | |
return unless text | |
text | |
.gsub( '\n', '\textbackslash n') | |
.gsub( '\r', '\textbackslash r') | |
.gsub( '~', '\textasciitilde ' ) | |
.gsub( '$', '\$' ) | |
.gsub( '^', '\textasciicircum ') | |
.gsub( '#', '\#' ) | |
.gsub( '%', '\%' ) | |
.gsub( '&', '\\\\\\&' ) | |
end | |
def escape_method(text) | |
return unless text | |
escape text .gsub( /\\/, '\textbackslash\-, ' ) | |
.gsub('{','\{') | |
.gsub('}','\}') | |
end | |
end | |
end; end | |
markdown = Redcarpet::Markdown.new(Redcarpet::Render::RusRubyBook, | |
no_intra_emphasis: true, | |
fenced_code_blocks: true, | |
lax_spacing: true) | |
if Dir.exist? ARGF.filename | |
Dir.chdir ARGF.filename do Dir.glob('*.md') do |path| | |
latex = markdown.render( File.read path ) | |
new_path = File.basename(path, '.md') + '.tex' | |
File.write new_path, latex, File::TRUNC & File::CREAT | |
end end | |
else | |
puts markdown.render(ARGF.read) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment