lib/asciidoctor/converter/manpage.rb in asciidoctor-1.5.7.1 vs lib/asciidoctor/converter/manpage.rb in asciidoctor-1.5.8
- old
+ new
@@ -18,23 +18,34 @@
LeadingPeriodRx = /^\./
EscapedMacroRx = /^(?:#{ESC}\\c\n)?#{ESC}\.((?:URL|MTO) ".*?" ".*?" )( |[^\s]*)(.*?)(?: *#{ESC}\\c)?$/
MockBoundaryRx = /<\/?BOUNDARY>/
EmDashCharRefRx = /—(?:​)?/
EllipsisCharRefRx = /…(?:​)?/
+ WrappedIndentRx = /#{CG_BLANK}*#{LF}#{CG_BLANK}*/
# Converts HTML entity references back to their original form, escapes
# special man characters and strips trailing whitespace.
#
# It's crucial that text only ever pass through manify once.
#
# str - the String to convert
# opts - an Hash of options to control processing (default: {})
- # * :preserve_space a Boolean that indicates whether to preserve spaces (only expanding tabs) if true
- # or to collapse all adjacent whitespace to a single space if false (default: true)
+ # * :whitespace an enum that indicates how to handle whitespace; supported options are:
+ # :preserve - preserve spaces (only expanding tabs); :normalize - normalize whitespace
+ # (remove spaces around newlines); :collapse - collapse adjacent whitespace to a single
+ # space (default: :collapse)
# * :append_newline a Boolean that indicates whether to append an endline to the result (default: false)
def manify str, opts = {}
- str = ((opts.fetch :preserve_space, true) ? (str.gsub TAB, ET) : (str.tr_s WHITESPACE, ' ')).
+ case opts.fetch :whitespace, :collapse
+ when :preserve
+ str = str.gsub TAB, ET
+ when :normalize
+ str = str.gsub WrappedIndentRx, LF
+ else
+ str = str.tr_s WHITESPACE, ' '
+ end
+ str = str.
gsub(LiteralBackslashRx, '\&(rs'). # literal backslash (not a troff escape sequence)
gsub(LeadingPeriodRx, '\\\&.'). # leading . is used in troff for macro call or other formatting; replace with \&.
# drop orphaned \c escape lines, unescape troff macro, quote adjacent character, isolate macro line
gsub(EscapedMacroRx) { (rest = $3.lstrip).empty? ? %(.#$1"#$2") : %(.#$1"#$2"#{LF}#{rest}) }.
gsub('-', '\-').
@@ -76,23 +87,25 @@
raise 'asciidoctor: ERROR: doctype must be set to manpage when using manpage backend'
end
mantitle = node.attr 'mantitle'
manvolnum = node.attr 'manvolnum', '1'
manname = node.attr 'manname', mantitle
+ manmanual = node.attr 'manmanual'
+ mansource = node.attr 'mansource'
docdate = (node.attr? 'reproducible') ? nil : (node.attr 'docdate')
# NOTE the first line enables the table (tbl) preprocessor, necessary for non-Linux systems
result = [%('\\" t
.\\" Title: #{mantitle}
.\\" Author: #{(node.attr? 'authors') ? (node.attr 'authors') : '[see the "AUTHOR(S)" section]'}
.\\" Generator: Asciidoctor #{node.attr 'asciidoctor-version'})]
result << %(.\\" Date: #{docdate}) if docdate
- result << %(.\\" Manual: #{(manual = node.attr 'manmanual') || '\ \&'}
-.\\" Source: #{(source = node.attr 'mansource') || '\ \&'}
+ result << %(.\\" Manual: #{manmanual ? (manmanual.tr_s WHITESPACE, ' ') : '\ \&'}
+.\\" Source: #{mansource ? (mansource.tr_s WHITESPACE, ' ') : '\ \&'}
.\\" Language: English
.\\")
# TODO add document-level setting to disable capitalization of manname
- result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{source ? (manify source) : '\ \&'}" "#{manual ? (manify manual) : '\ \&'}")
+ result << %(.TH "#{manify manname.upcase}" "#{manvolnum}" "#{docdate}" "#{mansource ? (manify mansource) : '\ \&'}" "#{manmanual ? (manify manmanual) : '\ \&'}")
# define portability settings
# see http://bugs.debian.org/507673
# see http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
result << '.ie \n(.g .ds Aq \(aq'
result << '.el .ds Aq \''
@@ -129,12 +142,12 @@
result << '.\}'
unless node.noheader
if node.attr? 'manpurpose'
mannames = node.attr 'mannames', [manname]
- result << %(.SH "#{(node.attr 'manname-title').upcase}"
-#{mannames.map {|n| manify n } * ', '} \\- #{manify node.attr 'manpurpose'})
+ result << %(.SH "#{(node.attr 'manname-title', 'NAME').upcase}"
+#{mannames.map {|n| manify n }.join ', '} \\- #{manify node.attr('manpurpose'), :whitespace => :normalize})
end
end
result << node.content
@@ -142,26 +155,25 @@
if node.footnotes? && !(node.attr? 'nofootnotes')
result << '.SH "NOTES"'
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
end
- # FIXME we really need an API that returns the authors as an array
- if (num_authors = (node.attr 'authorcount') || 0) > 0
- if num_authors == 1
- result << %(.SH "AUTHOR"
-.sp
-#{node.attr 'author'})
- else
+ unless (authors = node.authors).empty?
+ if authors.size > 1
result << '.SH "AUTHORS"'
- (1.upto num_authors).each do |i|
+ authors.each do |author|
result << %(.sp
-#{node.attr "author_#{i}"})
+#{author.name})
end
+ else
+ result << %(.SH "AUTHOR"
+.sp
+#{authors[0].name})
end
end
- result * LF
+ result.join LF
end
# NOTE embedded doesn't really make sense in the manpage backend
def embedded node
result = [node.content]
@@ -171,11 +183,11 @@
result.concat(node.footnotes.map {|fn| %(#{fn.index}. #{fn.text}) })
end
# QUESTION should we add an AUTHOR(S) section?
- result * LF
+ result.join LF
end
def section node
result = []
if node.level > 1
@@ -186,11 +198,11 @@
macro = 'SH'
stitle = node.title.upcase
end
result << %(.#{macro} "#{manify stitle}"
#{node.content})
- result * LF
+ result.join LF
end
def admonition node
result = []
result << %(.if n .sp
@@ -204,11 +216,11 @@
.ps -1
.br
#{resolve_content node}
.sp .5v
.RE)
- result * LF
+ result.join LF
end
alias audio skip_with_warning
def colist node
@@ -221,16 +233,16 @@
r lw(\n(.lu*75u/100u).'
num = 0
node.items.each do |item|
result << %(\\fB(#{num += 1})\\fP\\h'-2n':T{)
- result << (manify item.text)
+ result << (manify item.text, :whitespace => :normalize)
result << item.content if item.blocks?
result << 'T}'
end
result << '.TE'
- result * LF
+ result.join LF
end
# TODO implement horizontal (if it makes sense)
def dlist node
result = []
@@ -241,35 +253,35 @@
node.items.each do |terms, dd|
counter += 1
case node.style
when 'qanda'
result << %(.sp
-#{counter}. #{manify([*terms].map {|dt| dt.text } * ' ')}
+#{counter}. #{manify [*terms].map {|dt| dt.text }.join ' '}
.RS 4)
else
result << %(.sp
-#{manify([*terms].map {|dt| dt.text } * ', ')}
+#{manify [*terms].map {|dt| dt.text }.join(', '), :whitespace => :normalize}
.RS 4)
end
if dd
- result << (manify dd.text) if dd.text?
+ result << (manify dd.text, :whitespace => :normalize) if dd.text?
result << dd.content if dd.blocks?
end
result << '.RE'
end
- result * LF
+ result.join LF
end
def example node
result = []
result << %(.sp
.B #{manify node.captioned_title}
.br) if node.title?
result << %(.RS 4
#{resolve_content node}
.RE)
- result * LF
+ result.join LF
end
def floating_title node
%(.SS "#{manify node.title}")
end
@@ -282,28 +294,28 @@
.B #{manify node.captioned_title}
.br) if node.title?
result << %(.sp
.if n .RS 4
.nf
-#{manify node.content}
+#{manify node.content, :whitespace => :preserve}
.fi
.if n .RE)
- result * LF
+ result.join LF
end
def literal node
result = []
result << %(.sp
.B #{manify node.title}
.br) if node.title?
result << %(.sp
.if n .RS 4
.nf
-#{manify node.content}
+#{manify node.content, :whitespace => :preserve}
.fi
.if n .RE)
- result * LF
+ result.join LF
end
def olist node
result = []
result << %(.sp
@@ -318,15 +330,15 @@
.\\}
.el \\{\\
. sp -1
. IP " #{idx + 1}." 4.2
.\\}
-#{manify item.text})
+#{manify item.text, :whitespace => :normalize})
result << item.content if item.blocks?
result << '.RE'
end
- result * LF
+ result.join LF
end
def open node
case node.style
when 'abstract', 'partintro'
@@ -342,46 +354,44 @@
def paragraph node
if node.title?
%(.sp
.B #{manify node.title}
.br
-#{manify node.content})
+#{manify node.content, :whitespace => :normalize})
else
%(.sp
-#{manify node.content})
+#{manify node.content, :whitespace => :normalize})
end
end
alias preamble content
def quote node
result = []
if node.title?
result << %(.sp
-.in +.3i
+.RS 3
.B #{manify node.title}
.br
-.in)
+.RE)
end
attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
- result << %(.in +.3i
-.ll -.3i
-.nf
+ result << %(.RS 3
+.ll -.6i
#{resolve_content node}
-.fi
.br
-.in
+.RE
.ll)
if attribution_line
- result << %(.in +.5i
-.ll -.5i
+ result << %(.RS 5
+.ll -.10i
#{attribution_line}
-.in
+.RE
.ll)
end
- result * LF
+ result.join LF
end
alias sidebar skip_with_warning
def stem node
@@ -446,11 +456,11 @@
row_header[row_index][cell_index] << %(#{cell_halign}tB)
else
row_header[row_index][cell_index + 1] ||= []
row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
end
- row_text[row_index] << %(#{manify cell.text}#{LF})
+ row_text[row_index] << %(#{manify cell.text, :whitespace => :normalize}#{LF})
elsif tsec == :body
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
row_header[row_index][cell_index] << %(#{cell_halign}t)
else
row_header[row_index][cell_index + 1] ||= []
@@ -458,25 +468,25 @@
end
case cell.style
when :asciidoc
cell_content = cell.content
when :literal
- cell_content = %(.nf#{LF}#{manify cell.text}#{LF}.fi)
+ cell_content = %(.nf#{LF}#{manify cell.text, :whitespace => :preserve}#{LF}.fi)
when :verse
- cell_content = %(.nf#{LF}#{manify cell.text}#{LF}.fi)
+ cell_content = %(.nf#{LF}#{manify cell.text, :whitespace => :preserve}#{LF}.fi)
else
- cell_content = manify cell.content.join
+ cell_content = manify cell.content.join, :whitespace => :normalize
end
row_text[row_index] << %(#{cell_content}#{LF})
elsif tsec == :foot
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
row_header[row_index][cell_index] << %(#{cell_halign}tB)
else
row_header[row_index][cell_index + 1] ||= []
row_header[row_index][cell_index + 1] << %(#{cell_halign}tB)
end
- row_text[row_index] << %(#{manify cell.text}#{LF})
+ row_text[row_index] << %(#{manify cell.text, :whitespace => :normalize}#{LF})
end
if cell.colspan && cell.colspan > 1
(cell.colspan - 1).times do |i|
if row_header[row_index].empty? || row_header[row_index][cell_index].empty?
row_header[row_index][cell_index + i] << 'st'
@@ -509,17 +519,17 @@
end
#row_header.each do |row|
# result << LF
# row.each_with_index do |cell, i|
- # result << (cell * ' ')
+ # result << (cell.join ' ')
# result << ' ' if row.size > i + 1
# end
#end
# FIXME temporary fix to get basic table to display
result << LF
- result << row_header[0].map { 'lt' } * ' '
+ result << ('lt ' * row_header[0].size).chop
result << %(.#{LF})
row_text.each do |row|
result << row.join
end
@@ -548,15 +558,15 @@
.\\}
.el \\{\\
. sp -1
. IP \\(bu 2.3
.\\}
-#{manify item.text}]
+#{manify item.text, :whitespace => :normalize}]
result << item.content if item.blocks?
result << '.RE'
}
- result * LF
+ result.join LF
end
# FIXME git uses [verse] for the synopsis; detect this special case
def verse node
result = []
@@ -567,32 +577,32 @@
end
attribution_line = (node.attr? 'citetitle') ? %(#{node.attr 'citetitle'} ) : nil
attribution_line = (node.attr? 'attribution') ? %[#{attribution_line}\\(em #{node.attr 'attribution'}] : nil
result << %(.sp
.nf
-#{manify node.content}
+#{manify node.content, :whitespace => :preserve}
.fi
.br)
if attribution_line
result << %(.in +.5i
.ll -.5i
#{attribution_line}
.in
.ll)
end
- result * LF
+ result.join LF
end
def video node
start_param = (node.attr? 'start', nil, false) ? %(&start=#{node.attr 'start'}) : ''
end_param = (node.attr? 'end', nil, false) ? %(&end=#{node.attr 'end'}) : ''
result = []
result << %(.sp
.B #{manify node.title}
.br) if node.title?
result << %(<#{node.media_uri(node.attr 'target')}#{start_param}#{end_param}> (video))
- result * LF
+ result.join LF
end
def inline_anchor node
target = node.target
case node.type
@@ -653,19 +663,19 @@
def inline_kbd node
if (keys = node.attr 'keys').size == 1
keys[0]
else
- keys * %(#{ESC_BS}0+#{ESC_BS}0)
+ keys.join %(#{ESC_BS}0+#{ESC_BS}0)
end
end
def inline_menu node
caret = %[#{ESC_BS}0#{ESC_BS}(fc#{ESC_BS}0]
menu = node.attr 'menu'
if !(submenus = node.attr 'submenus').empty?
- submenu_path = submenus.map {|item| %(#{ESC_BS}fI#{item}#{ESC_BS}fP) } * caret
+ submenu_path = submenus.map {|item| %(#{ESC_BS}fI#{item}#{ESC_BS}fP) }.join caret
%(#{ESC_BS}fI#{menu}#{ESC_BS}fP#{caret}#{submenu_path}#{caret}#{ESC_BS}fI#{node.attr 'menuitem'}#{ESC_BS}fP)
elsif (menuitem = node.attr 'menuitem')
%(#{ESC_BS}fI#{menu}#{caret}#{menuitem}#{ESC_BS}fP)
else
%(#{ESC_BS}fI#{menu}#{ESC_BS}fP)
@@ -689,10 +699,10 @@
node.text
end
end
def resolve_content node
- node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content})
+ node.content_model == :compound ? node.content : %(.sp#{LF}#{manify node.content, :whitespace => :normalize})
end
def write_alternate_pages mannames, manvolnum, target
if mannames && mannames.size > 1
mannames.shift