Skip to content

Instantly share code, notes, and snippets.

@mojavelinux
Created October 7, 2014 19:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mojavelinux/a086e542a145a7709b98 to your computer and use it in GitHub Desktop.
Save mojavelinux/a086e542a145a7709b98 to your computer and use it in GitHub Desktop.
Asciidoctor manpage converter
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('&lt;', '<')
.gsub('&gt;', '>')
.gsub('&#169;', '\\(co')
.gsub('&#174;', '\\(rg')
.gsub('&#8482;', '\\(tm')
.gsub('&#8201;', ' ') #.gsub('&#8201;&#8212;&#8201;', ' \\(em ')
.gsub('&#8212;', '\\(em')
.gsub('&#8230;', '...') #.gsub('&#8230;', '\\\&...')
.gsub('&#8217;', '\\(cq')
.gsub('&#8592;', '\\(<-')
.gsub('&#8594;', '\\(->')
.gsub('&#8656;', '\\(lA')
.gsub('&#8658;', '\\(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