Created
October 7, 2014 19:57
-
-
Save mojavelinux/a086e542a145a7709b98 to your computer and use it in GitHub Desktop.
Asciidoctor manpage converter
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
require 'asciidoctor' | |
# TODO | |
# * manually insert "NAME" section | |
# * still getting extra blank lines in definition lists | |
class ManpageConverter | |
include Asciidoctor::Converter | |
register_for 'manpage' | |
EOL = "\n" | |
def convert node, transform = nil | |
transform ||= node.node_name | |
send transform, node if respond_to? transform | |
end | |
def document node | |
result = %['\\" t | |
.\\" Title: #{node.attr 'mantitle'} | |
.\\" Author: #{(node.attr? 'author') ? (node.attr 'author') : '[see the "AUTHORS" section]'} | |
.\\" Generator: Asciidoctor #{node.attr 'asciidoctor-version'} | |
.\\" Date: #{node.attr 'docdate'} | |
.\\" Manual: #{(manual = (node.attr? 'manmanual') ? (node.attr 'manmanual') : '\\ \\&')} | |
.\\" Source: #{(source = (node.attr? 'mansource') ? (node.attr 'mansource') : '\\ \\&')} | |
.\\" Language: English | |
.\\" | |
.TH "#{(node.attr 'manname').upcase}" "#{node.attr 'manvolnum'}" "#{node.attr 'docdate'}" "#{manify source, false}" "#{manify manual, false}" | |
.ie \\n(.g .ds Aq \\(aq | |
.el .ds Aq ' | |
.nh | |
.ad l] | |
result = %(#{result}\n#{node.content}) | |
if node.attr? 'author' | |
result = %(#{result} | |
.SH "AUTHOR" | |
.PP | |
\\fB#{node.attr 'author'}\\fR | |
.RE) | |
end | |
result | |
end | |
def section node | |
title = node.title | |
%(.S#{node.level > 1 ? 'S' : 'H'} "#{node.level > 1 ? title : title.upcase}" | |
#{title.upcase == 'NAME' ? node.content.gsub(/\A\.sp\n/, '') : node.content}) | |
end | |
def paragraph node | |
%(.sp | |
#{manify node.content}) | |
end | |
# TODO needs cleanup | |
def dlist node | |
result = [] | |
node.items.each do |terms, dd| | |
# TODO support multiple terms per item | |
result << (manify [*terms].first.text, !!dd) | |
if dd | |
result << %(.RS 4 | |
#{manify dd.text, false}) | |
if dd.blocks? | |
result << dd.content | |
else | |
result << EOL | |
end | |
result << '.RE' | |
end | |
end | |
result * EOL | |
end | |
def ulist node | |
node.items.map {|li| | |
%[.sp | |
.RS 4 | |
.ie n \\{\\ | |
\\h'-04'\\(bu\\h'+03'\\c | |
.\\} | |
.el \\{\\ | |
.sp -1 | |
.IP \\(bu 2.3 | |
.\\} | |
#{manify li.text, false}#{li.blocks? ? li.content : nil} | |
.RE] | |
} * EOL | |
end | |
def inline_anchor node | |
target = node.target | |
case node.type | |
when :link | |
if target.start_with? 'mailto:' | |
target[7..-1] | |
else | |
target | |
end | |
else | |
target | |
end | |
end | |
def inline_quoted node | |
case node.type | |
when :emphasis | |
%[\\fI#{node.text}\\fR] | |
when :strong | |
%[\\fB#{node.text}\\fR] | |
when :single | |
%[\\(oq#{node.text}\\(cq] | |
when :double | |
%[\\(lq#{node.text}\\(rq] | |
else | |
node.text | |
end | |
end | |
# Folds each endline into a single space, escapes special man characters, | |
# reverts HTML entity references back to their original form, strips trailing | |
# whitespace and, optionally, appends a newline | |
# see: man groff_char | |
def manify str, append_newline = true, preserve_space = false | |
if preserve_space | |
str = str.gsub("\t", (' ' * 8)) | |
else | |
str = str.tr_s("\n\t ", ' ') | |
end | |
str | |
.gsub(/\./, '\\\&.') | |
.gsub('-', '\\-') | |
.gsub('<', '<') | |
.gsub('>', '>') | |
.gsub('©', '\\(co') | |
.gsub('®', '\\(rg') | |
.gsub('™', '\\(tm') | |
.gsub(' ', ' ') #.gsub(' — ', ' \\(em ') | |
.gsub('—', '\\(em') | |
.gsub('…', '...') #.gsub('…', '\\\&...') | |
.gsub('’', '\\(cq') | |
.gsub('←', '\\(<-') | |
.gsub('→', '\\(->') | |
.gsub('⇐', '\\(lA') | |
.gsub('⇒', '\\(rA') | |
.gsub('\'', '\\(aq') | |
.rstrip + (append_newline ? EOL : '') | |
end | |
end | |
#options = {} | |
#options[:header_footer] = true | |
#options[:backend] = :manpage | |
#Asciidoctor.convert_file ARGV.first, options |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment