1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00

Renamespace lib/rdoc/markup from SM::SimpleMarkup to RDoc::Markup.

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@15033 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
drbrain 2008-01-14 03:34:05 +00:00
parent cbd4604c53
commit fcb0b1f503
31 changed files with 2573 additions and 2863 deletions

View file

@ -1,2 +0,0 @@
simple_markup
simple_markup.rb

View file

@ -1,6 +1,7 @@
require 'rdoc/markup/simple_markup/lines.rb'
require 'rdoc/markup'
require 'rdoc/markup/lines'
module SM
class RDoc::Markup
##
# A Fragment is a chunk of text, subclassed as a paragraph, a list
@ -119,7 +120,7 @@ module SM
##
# Collect groups of lines together. Each group will end up containing a flow
# of text
# of text.
class LineCollection

View file

@ -1,8 +1,10 @@
module SM
require 'rdoc/markup'
class RDoc::Markup
##
# We manage a set of attributes. Each attribute has a symbol name and a bit
# value
# value.
class Attribute
SPECIAL = 1
@ -39,7 +41,7 @@ module SM
##
# An AttrChanger records a change in attributes. It contains a bitmap of the
# attributes to turn on, and a bitmap of those to turn off
# attributes to turn on, and a bitmap of those to turn off.
AttrChanger = Struct.new(:turn_on, :turn_off)
@ -50,7 +52,7 @@ module SM
end
##
# An array of attributes which parallels the characters in a string
# An array of attributes which parallels the characters in a string.
class AttrSpan
def initialize(length)
@ -84,12 +86,12 @@ module SM
end
def to_s
"Special: type=#{type}, name=#{SM::Attribute.as_string type}, text=#{text.dump}"
"Special: type=#{type}, name=#{RDoc::Markup::Attribute.as_string type}, text=#{text.dump}"
end
def inspect
"#<SM::Special:0x%x @type=%p, name=%p @text=%p>" % [
object_id, @type, SM::Attribute.as_string(type), text.dump]
"#<RDoc::Markup::Special:0x%x @type=%p, name=%p @text=%p>" % [
object_id, @type, RDoc::Markup::Attribute.as_string(type), text.dump]
end
end

View file

@ -1,4 +1,4 @@
module SM
class RDoc::Markup
##
# We store the lines we're working on as objects of class Line. These
@ -14,7 +14,7 @@ module SM
RULE = :RULE
PARAGRAPH = :PARAGRAPH
VERBATIM = :VERBATIM
# line type
attr_accessor :type
@ -36,7 +36,6 @@ module SM
# true if this line has been deleted from the list of lines
attr_accessor :deleted
def initialize(text)
@text = text.dup
@ -69,7 +68,6 @@ module SM
##
# Strip off the leading margin
#
def strip_leading(size)
if @text.size > size
@ -85,7 +83,7 @@ module SM
end
##
# A container for all the lines
# A container for all the lines.
class Lines

View file

@ -0,0 +1,71 @@
require 'rdoc/markup'
##
# Handle common directives that can occur in a block of text:
#
# : include : filename
class RDoc::Markup::PreProcess
def initialize(input_file_name, include_path)
@input_file_name = input_file_name
@include_path = include_path
end
##
# Look for common options in a chunk of text. Options that we don't handle
# are passed back to our caller as |directive, param|
def handle(text)
text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
prefix = $1
directive = $2.downcase
param = $3
case directive
when "include"
filename = param.split[0]
include_file(filename, prefix)
else
yield(directive, param)
end
end
end
private
##
# Include a file, indenting it correctly.
def include_file(name, indent)
if full_name = find_include_file(name) then
content = File.open(full_name) {|f| f.read}
# strip leading '#'s, but only if all lines start with them
if content =~ /^[^#]/
content.gsub(/^/, indent)
else
content.gsub(/^#?/, indent)
end
else
$stderr.puts "Couldn't find file to include: '#{name}'"
''
end
end
##
# Look for the given file in the directory containing the current file,
# and then in each of the directories specified in the RDOC_INCLUDE path
def find_include_file(name)
to_search = [ File.dirname(@input_file_name) ].concat @include_path
to_search.each do |dir|
full_name = File.join(dir, name)
stat = File.stat(full_name) rescue next
return full_name if stat.readable?
end
nil
end
end

View file

@ -1,463 +0,0 @@
require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/lines.rb'
##
# SimpleMarkup parses plain text documents and attempts to decompose
# them into their constituent parts. Some of these parts are high-level:
# paragraphs, chunks of verbatim text, list entries and the like. Other
# parts happen at the character level: a piece of bold text, a word in
# code font. This markup is similar in spirit to that used on WikiWiki
# webs, where folks create web pages using a simple set of formatting
# rules.
#
# SimpleMarkup itself does no output formatting: this is left to a
# different set of classes.
#
# SimpleMarkup is extendable at runtime: you can add new markup
# elements to be recognised in the documents that SimpleMarkup parses.
#
# SimpleMarkup is intended to be the basis for a family of tools which
# share the common requirement that simple, plain-text should be
# rendered in a variety of different output formats and media. It is
# envisaged that SimpleMarkup could be the basis for formating RDoc
# style comment blocks, Wiki entries, and online FAQs.
#
# = Basic Formatting
#
# * SimpleMarkup looks for a document's natural left margin. This is
# used as the initial margin for the document.
#
# * Consecutive lines starting at this margin are considered to be a
# paragraph.
#
# * If a paragraph starts with a "*", "-", or with "<digit>.", then it is
# taken to be the start of a list. The margin in increased to be the
# first non-space following the list start flag. Subsequent lines
# should be indented to this new margin until the list ends. For
# example:
#
# * this is a list with three paragraphs in
# the first item. This is the first paragraph.
#
# And this is the second paragraph.
#
# 1. This is an indented, numbered list.
# 2. This is the second item in that list
#
# This is the third conventional paragraph in the
# first list item.
#
# * This is the second item in the original list
#
# * You can also construct labeled lists, sometimes called description
# or definition lists. Do this by putting the label in square brackets
# and indenting the list body:
#
# [cat] a small furry mammal
# that seems to sleep a lot
#
# [ant] a little insect that is known
# to enjoy picnics
#
# A minor variation on labeled lists uses two colons to separate the
# label from the list body:
#
# cat:: a small furry mammal
# that seems to sleep a lot
#
# ant:: a little insect that is known
# to enjoy picnics
#
# This latter style guarantees that the list bodies' left margins are
# aligned: think of them as a two column table.
#
# * Any line that starts to the right of the current margin is treated
# as verbatim text. This is useful for code listings. The example of a
# list above is also verbatim text.
#
# * A line starting with an equals sign (=) is treated as a
# heading. Level one headings have one equals sign, level two headings
# have two,and so on.
#
# * A line starting with three or more hyphens (at the current indent)
# generates a horizontal rule. THe more hyphens, the thicker the rule
# (within reason, and if supported by the output device)
#
# * You can use markup within text (except verbatim) to change the
# appearance of parts of that text. Out of the box, SimpleMarkup
# supports word-based and general markup.
#
# Word-based markup uses flag characters around individual words:
#
# [\*word*] displays word in a *bold* font
# [\_word_] displays word in an _emphasized_ font
# [\+word+] displays word in a +code+ font
#
# General markup affects text between a start delimiter and and end
# delimiter. Not surprisingly, these delimiters look like HTML markup.
#
# [\<b>text...</b>] displays word in a *bold* font
# [\<em>text...</em>] displays word in an _emphasized_ font
# [\<i>text...</i>] displays word in an _emphasized_ font
# [\<tt>text...</tt>] displays word in a +code+ font
#
# Unlike conventional Wiki markup, general markup can cross line
# boundaries. You can turn off the interpretation of markup by
# preceding the first character with a backslash, so \\\<b>bold
# text</b> and \\\*bold* produce \<b>bold text</b> and \*bold
# respectively.
#
# = Using SimpleMarkup
#
# For information on using SimpleMarkup programatically, see SM::SimpleMarkup.
#--
# Author:: Dave Thomas, dave@pragmaticprogrammer.com
# Version:: 0.0
# License:: Ruby license
module SM
# == Synopsis
#
# This code converts <tt>input_string</tt>, which is in the format
# described in markup/simple_markup.rb, to HTML. The conversion
# takes place in the +convert+ method, so you can use the same
# SimpleMarkup object to convert multiple input strings.
#
# require 'rdoc/markup/simple_markup'
# require 'rdoc/markup/simple_markup/to_html'
#
# p = SM::SimpleMarkup.new
# h = SM::ToHtml.new
#
# puts p.convert(input_string, h)
#
# You can extend the SimpleMarkup parser to recognise new markup
# sequences, and to add special processing for text that matches a
# regular epxression. Here we make WikiWords significant to the parser,
# and also make the sequences {word} and \<no>text...</no> signify
# strike-through text. When then subclass the HTML output class to deal
# with these:
#
# require 'rdoc/markup/simple_markup'
# require 'rdoc/markup/simple_markup/to_html'
#
# class WikiHtml < SM::ToHtml
# def handle_special_WIKIWORD(special)
# "<font color=red>" + special.text + "</font>"
# end
# end
#
# p = SM::SimpleMarkup.new
# p.add_word_pair("{", "}", :STRIKE)
# p.add_html("no", :STRIKE)
#
# p.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
#
# h = WikiHtml.new
# h.add_tag(:STRIKE, "<strike>", "</strike>")
#
# puts "<body>" + p.convert(ARGF.read, h) + "</body>"
class SimpleMarkup
SPACE = ?\s
# List entries look like:
# * text
# 1. text
# [label] text
# label:: text
#
# Flag it as a list entry, and work out the indent for subsequent lines
SIMPLE_LIST_RE = /^(
( \* (?# bullet)
|- (?# bullet)
|\d+\. (?# numbered )
|[A-Za-z]\. (?# alphabetically numbered )
)
\s+
)\S/x
LABEL_LIST_RE = /^(
( \[.*?\] (?# labeled )
|\S.*:: (?# note )
)(?:\s+|$)
)/x
##
# Take a block of text and use various heuristics to determine it's
# structure (paragraphs, lists, and so on). Invoke an event handler as we
# identify significant chunks.
def initialize
@am = AttributeManager.new
@output = nil
end
##
# Add to the sequences used to add formatting to an individual word (such
# as *bold*). Matching entries will generate attibutes that the output
# formatters can recognize by their +name+.
def add_word_pair(start, stop, name)
@am.add_word_pair(start, stop, name)
end
##
# Add to the sequences recognized as general markup.
def add_html(tag, name)
@am.add_html(tag, name)
end
##
# Add to other inline sequences. For example, we could add WikiWords using
# something like:
#
# parser.add_special(/\b([A-Z][a-z]+[A-Z]\w+)/, :WIKIWORD)
#
# Each wiki word will be presented to the output formatter via the
# accept_special method.
def add_special(pattern, name)
@am.add_special(pattern, name)
end
##
# We take a string, split it into lines, work out the type of each line,
# and from there deduce groups of lines (for example all lines in a
# paragraph). We then invoke the output formatter using a Visitor to
# display the result.
def convert(str, op)
@lines = Lines.new(str.split(/\r?\n/).collect { |aLine|
Line.new(aLine) })
return "" if @lines.empty?
@lines.normalize
assign_types_to_lines
group = group_lines
# call the output formatter to handle the result
# group.to_a.each {|i| p i}
group.accept(@am, op)
end
private
##
# Look through the text at line indentation. We flag each line as being
# Blank, a paragraph, a list element, or verbatim text.
def assign_types_to_lines(margin = 0, level = 0)
while line = @lines.next
if line.isBlank?
line.stamp(Line::BLANK, level)
next
end
# if a line contains non-blanks before the margin, then it must belong
# to an outer level
text = line.text
for i in 0...margin
if text[i] != SPACE
@lines.unget
return
end
end
active_line = text[margin..-1]
# Rules (horizontal lines) look like
#
# --- (three or more hyphens)
#
# The more hyphens, the thicker the rule
#
if /^(---+)\s*$/ =~ active_line
line.stamp(Line::RULE, level, $1.length-2)
next
end
# Then look for list entries. First the ones that have to have
# text following them (* xxx, - xxx, and dd. xxx)
if SIMPLE_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
prefix_length = prefix.length
flag = case prefix
when "*","-" then ListBase::BULLET
when /^\d/ then ListBase::NUMBER
when /^[A-Z]/ then ListBase::UPPERALPHA
when /^[a-z]/ then ListBase::LOWERALPHA
else raise "Invalid List Type: #{self.inspect}"
end
line.stamp(Line::LIST, level+1, prefix, flag)
text[margin, prefix_length] = " " * prefix_length
assign_types_to_lines(offset, level + 1)
next
end
if LABEL_LIST_RE =~ active_line
offset = margin + $1.length
prefix = $2
prefix_length = prefix.length
next if handled_labeled_list(line, level, margin, offset, prefix)
end
# Headings look like
# = Main heading
# == Second level
# === Third
#
# Headings reset the level to 0
if active_line[0] == ?= and active_line =~ /^(=+)\s*(.*)/
prefix_length = $1.length
prefix_length = 6 if prefix_length > 6
line.stamp(Line::HEADING, 0, prefix_length)
line.strip_leading(margin + prefix_length)
next
end
# If the character's a space, then we have verbatim text,
# otherwise
if active_line[0] == SPACE
line.strip_leading(margin) if margin > 0
line.stamp(Line::VERBATIM, level)
else
line.stamp(Line::PARAGRAPH, level)
end
end
end
##
# Handle labeled list entries, We have a special case to deal with.
# Because the labels can be long, they force the remaining block of text
# over the to right:
#
# this is a long label that I wrote:: and here is the
# block of text with
# a silly margin
#
# So we allow the special case. If the label is followed by nothing, and
# if the following line is indented, then we take the indent of that line
# as the new margin.
#
# this is a long label that I wrote::
# here is a more reasonably indented block which
# will be attached to the label.
#
def handled_labeled_list(line, level, margin, offset, prefix)
prefix_length = prefix.length
text = line.text
flag = nil
case prefix
when /^\[/
flag = ListBase::LABELED
prefix = prefix[1, prefix.length-2]
when /:$/
flag = ListBase::NOTE
prefix.chop!
else raise "Invalid List Type: #{self.inspect}"
end
# body is on the next line
if text.length <= offset
original_line = line
line = @lines.next
return(false) unless line
text = line.text
for i in 0..margin
if text[i] != SPACE
@lines.unget
return false
end
end
i = margin
i += 1 while text[i] == SPACE
if i >= text.length
@lines.unget
return false
else
offset = i
prefix_length = 0
@lines.delete(original_line)
end
end
line.stamp(Line::LIST, level+1, prefix, flag)
text[margin, prefix_length] = " " * prefix_length
assign_types_to_lines(offset, level + 1)
return true
end
##
# Return a block consisting of fragments which are paragraphs, list
# entries or verbatim text. We merge consecutive lines of the same type
# and level together. We are also slightly tricky with lists: the lines
# following a list introduction look like paragraph lines at the next
# level, and we remap them into list entries instead.
def group_lines
@lines.rewind
inList = false
wantedType = wantedLevel = nil
block = LineCollection.new
group = nil
while line = @lines.next
if line.level == wantedLevel and line.type == wantedType
group.add_text(line.text)
else
group = block.fragment_for(line)
block.add(group)
if line.type == Line::LIST
wantedType = Line::PARAGRAPH
else
wantedType = line.type
end
wantedLevel = line.type == Line::HEADING ? line.param : line.level
end
end
block.normalize
block
end
##
# For debugging, we allow access to our line contents as text.
def content
@lines.as_text
end
public :content
##
# For debugging, return the list of line types.
def get_line_types
@lines.line_types
end
public :get_line_types
end
end

View file

@ -1,73 +0,0 @@
module SM
##
# Handle common directives that can occur in a block of text:
#
# : include : filename
class PreProcess
def initialize(input_file_name, include_path)
@input_file_name = input_file_name
@include_path = include_path
end
##
# Look for common options in a chunk of text. Options that we don't handle
# are passed back to our caller as |directive, param|
def handle(text)
text.gsub!(/^([ \t#]*):(\w+):\s*(.+)?\n/) do
prefix = $1
directive = $2.downcase
param = $3
case directive
when "include"
filename = param.split[0]
include_file(filename, prefix)
else
yield(directive, param)
end
end
end
private
##
# Include a file, indenting it correctly.
def include_file(name, indent)
if (full_name = find_include_file(name))
content = File.open(full_name) {|f| f.read}
# strip leading '#'s, but only if all lines start with them
if content =~ /^[^#]/
content.gsub(/^/, indent)
else
content.gsub(/^#?/, indent)
end
else
$stderr.puts "Couldn't find file to include: '#{name}'"
''
end
end
##
# Look for the given file in the directory containing the current file,
# and then in each of the directories specified in the RDOC_INCLUDE path
def find_include_file(name)
to_search = [ File.dirname(@input_file_name) ].concat @include_path
to_search.each do |dir|
full_name = File.join(dir, name)
stat = File.stat(full_name) rescue next
return full_name if stat.readable?
end
nil
end
end
end

View file

@ -1,287 +0,0 @@
require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/inline'
require 'cgi'
module SM
class ToHtml
LIST_TYPE_TO_HTML = {
ListBase::BULLET => [ "<ul>", "</ul>" ],
ListBase::NUMBER => [ "<ol>", "</ol>" ],
ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
ListBase::LABELED => [ "<dl>", "</dl>" ],
ListBase::NOTE => [ "<table>", "</table>" ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
end
##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@attr_tags = [
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
]
end
##
# Add a new set of HTML tags for an attribute. We allow separate start and
# end tags for flexibility.
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
end
##
# Given an HTML tag, decorate it with class information and the like if
# required. This is a no-op in the base class, but is overridden in HTML
# output classes that implement style sheets.
def annotate(tag)
tag
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << annotate("<p>") + "\n"
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << annotate("</p>") + "\n"
end
def accept_verbatim(am, fragment)
@res << annotate("<pre>") + "\n"
@res << CGI.escapeHTML(fragment.txt)
@res << annotate("</pre>") << "\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "<hr size=\"#{size}\"></hr>"
end
def accept_list_start(am, fragment)
@res << html_list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << annotate(tag) << "\n"
end
@res << html_list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << annotate(tag) << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << annotate("<p />") << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
end
private
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.on)
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.off)
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# some of these patterns are taken from SmartyPants...
def convert_string(item)
CGI.escapeHTML(item).
# convert -- to em-dash, (-- to en-dash)
gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
# convert single opening quote
gsub(/'/, '&#8216;').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
# convert double opening quote
gsub(/'/, '&#8220;').
# convert copyright
gsub(/\(c\)/, '&#169;').
# convert and registered trademark
gsub(/\(r\)/, '&#174;')
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
annotate("<h#{level}>") +
convert_flow(flow) +
annotate("</h#{level}>\n")
end
def html_list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
annotate(tags[ is_open_tag ? 0 : 1])
end
def list_item_start(am, fragment)
case fragment.type
when ListBase::BULLET, ListBase::NUMBER
annotate("<li>")
when ListBase::UPPERALPHA
annotate("<li type=\"A\">")
when ListBase::LOWERALPHA
annotate("<li type=\"a\">")
when ListBase::LABELED
annotate("<dt>") +
convert_flow(am.flow(fragment.param)) +
annotate("</dt>") +
annotate("<dd>")
when ListBase::NOTE
annotate("<tr>") +
annotate("<td valign=\"top\">") +
convert_flow(am.flow(fragment.param)) +
annotate("</td>") +
annotate("<td>")
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
ListBase::LOWERALPHA
"</li>"
when ListBase::LABELED
"</dd>"
when ListBase::NOTE
"</td></tr>"
else
raise "Invalid list type"
end
end
end
end

View file

@ -1,332 +0,0 @@
require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/inline'
require 'cgi'
module SM
# Convert SimpleMarkup to basic LaTeX report format
class ToLaTeX
BS = "\020" # \
OB = "\021" # {
CB = "\022" # }
DL = "\023" # Dollar
BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
HAT = "#{BS}symbol#{OB}94#{CB}"
BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
TILDE = "#{DL}#{BS}sim#{DL}"
LESSTHAN = "#{DL}<#{DL}"
GREATERTHAN = "#{DL}>#{DL}"
def self.l(str)
str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
end
def l(arg)
SM::ToLaTeX.l(arg)
end
LIST_TYPE_TO_LATEX = {
ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
ListBase::NOTE => [
l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
l("\\end{tabularx}") ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
@list_depth = 0
@prev_list_types = []
end
##
# Set up the standard mapping of attributes to LaTeX
def init_tags
@attr_tags = [
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
InlineTag.new(SM::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
InlineTag.new(SM::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
]
end
##
# Escape a LaTeX string
def escape(str)
# $stderr.print "FE: ", str
s = str.
# sub(/\s+$/, '').
gsub(/([_\${}&%#])/, "#{BS}\\1").
gsub(/\\/, BACKSLASH).
gsub(/\^/, HAT).
gsub(/~/, TILDE).
gsub(/</, LESSTHAN).
gsub(/>/, GREATERTHAN).
gsub(/,,/, ",{},").
gsub(/\`/, BACKQUOTE)
# $stderr.print "-> ", s, "\n"
s
end
##
# Add a new set of LaTeX tags for an attribute. We allow
# separate start and end tags for flexibility
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
end
def accept_paragraph(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << "\n"
end
def accept_verbatim(am, fragment)
@res << "\n\\begin{code}\n"
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
@res << "\n\\end{code}\n\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
end
def accept_list_start(am, fragment)
@res << list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << tag << "\n"
end
@res << list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << tag << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
end
private
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << tag.on
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << tag.off
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
# $stderr.puts "Converting '#{item}'"
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# some of these patterns are taken from SmartyPants...
def convert_string(item)
escape(item).
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
gsub(%r{\'(?=\W|s\b)}) { "'" }.
# convert single opening quote
gsub(/'/, '`').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
# convert double opening quote
gsub(/"/, "``").
# convert copyright
gsub(/\(c\)/, '\copyright{}')
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
case level
when 1 then "\\chapter{"
when 2 then "\\section{"
when 3 then "\\subsection{"
when 4 then "\\subsubsection{"
else "\\paragraph{"
end +
convert_flow(flow) +
"}\n"
end
def list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
if tags[2] # enumerate
if is_open_tag
@list_depth += 1
if @prev_list_types[@list_depth] != tags[2]
case @list_depth
when 1
roman = "i"
when 2
roman = "ii"
when 3
roman = "iii"
when 4
roman = "iv"
else
raise("Too deep list: level #{@list_depth}")
end
@prev_list_types[@list_depth] = tags[2]
return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
end
else
@list_depth -= 1
end
end
tags[ is_open_tag ? 0 : 1]
end
def list_item_start(am, fragment)
case fragment.type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
ListBase::LOWERALPHA
"\\item "
when ListBase::LABELED
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
when ListBase::NOTE
convert_flow(am.flow(fragment.param)) + " & "
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
ListBase::LOWERALPHA, ListBase::LABELED
""
when ListBase::NOTE
"\\\\\n"
else
raise "Invalid list type"
end
end
end
end

View file

@ -1,8 +1,8 @@
require 'rdoc/markup/simple_markup/fragments'
require 'rdoc/markup/simple_markup/inline'
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
module SM
class RDoc::Markup
module Flow
P = Struct.new(:body)
@ -24,12 +24,12 @@ module SM
class ToFlow
LIST_TYPE_TO_HTML = {
SM::ListBase::BULLET => [ "<ul>", "</ul>" ],
SM::ListBase::NUMBER => [ "<ol>", "</ol>" ],
SM::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
SM::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
SM::ListBase::LABELED => [ "<dl>", "</dl>" ],
SM::ListBase::NOTE => [ "<table>", "</table>" ],
RDoc::Markup::ListBase::BULLET => [ "<ul>", "</ul>" ],
RDoc::Markup::ListBase::NUMBER => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::LABELED => [ "<dl>", "</dl>" ],
RDoc::Markup::ListBase::NOTE => [ "<table>", "</table>" ],
}
InlineTag = Struct.new(:bit, :on, :off)
@ -43,9 +43,9 @@ module SM
def init_tags
@attr_tags = [
InlineTag.new(SM::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(SM::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(SM::Attribute.bitmap_for(:EM), "<em>", "</em>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
]
end
@ -54,7 +54,7 @@ module SM
# end tags for flexibility
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(SM::Attribute.bitmap_for(name), start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##

286
lib/rdoc/markup/to_html.rb Normal file
View file

@ -0,0 +1,286 @@
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
class RDoc::Markup::ToHtml
LIST_TYPE_TO_HTML = {
RDoc::Markup::ListBase::BULLET => [ "<ul>", "</ul>" ],
RDoc::Markup::ListBase::NUMBER => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::UPPERALPHA => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::LOWERALPHA => [ "<ol>", "</ol>" ],
RDoc::Markup::ListBase::LABELED => [ "<dl>", "</dl>" ],
RDoc::Markup::ListBase::NOTE => [ "<table>", "</table>" ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
end
##
# Set up the standard mapping of attributes to HTML tags
def init_tags
@attr_tags = [
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
]
end
##
# Add a new set of HTML tags for an attribute. We allow separate start and
# end tags for flexibility.
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##
# Given an HTML tag, decorate it with class information and the like if
# required. This is a no-op in the base class, but is overridden in HTML
# output classes that implement style sheets.
def annotate(tag)
tag
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res
end
def accept_paragraph(am, fragment)
@res << annotate("<p>") + "\n"
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << annotate("</p>") + "\n"
end
def accept_verbatim(am, fragment)
@res << annotate("<pre>") + "\n"
@res << CGI.escapeHTML(fragment.txt)
@res << annotate("</pre>") << "\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "<hr size=\"#{size}\"></hr>"
end
def accept_list_start(am, fragment)
@res << html_list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << annotate(tag) << "\n"
end
@res << html_list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << annotate(tag) << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << annotate("<p />") << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
end
private
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.on)
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << annotate(tag.off)
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
res << convert_string(item)
when RDoc::Markup::AttrChanger
off_tags(res, item)
on_tags(res, item)
when RDoc::Markup::Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# some of these patterns are taken from SmartyPants...
def convert_string(item)
CGI.escapeHTML(item).
# convert -- to em-dash, (-- to en-dash)
gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1&#8217;" }.
gsub(%r{\'(?=\W|s\b)}) { "&#8217;" }.
# convert single opening quote
gsub(/'/, '&#8216;').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}) { "#$1&#8221;" }.
# convert double opening quote
gsub(/'/, '&#8220;').
# convert copyright
gsub(/\(c\)/, '&#169;').
# convert and registered trademark
gsub(/\(r\)/, '&#174;')
end
def convert_special(special)
handled = false
RDoc::Markup::Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
annotate("<h#{level}>") +
convert_flow(flow) +
annotate("</h#{level}>\n")
end
def html_list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
annotate(tags[ is_open_tag ? 0 : 1])
end
def list_item_start(am, fragment)
case fragment.type
when RDoc::Markup::ListBase::BULLET, RDoc::Markup::ListBase::NUMBER then
annotate("<li>")
when RDoc::Markup::ListBase::UPPERALPHA then
annotate("<li type=\"A\">")
when RDoc::Markup::ListBase::LOWERALPHA then
annotate("<li type=\"a\">")
when RDoc::Markup::ListBase::LABELED then
annotate("<dt>") +
convert_flow(am.flow(fragment.param)) +
annotate("</dt>") +
annotate("<dd>")
when RDoc::Markup::ListBase::NOTE then
annotate("<tr>") +
annotate("<td valign=\"top\">") +
convert_flow(am.flow(fragment.param)) +
annotate("</td>") +
annotate("<td>")
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when RDoc::Markup::ListBase::BULLET, RDoc::Markup::ListBase::NUMBER,
RDoc::Markup::ListBase::UPPERALPHA,
RDoc::Markup::ListBase::LOWERALPHA then
"</li>"
when RDoc::Markup::ListBase::LABELED then
"</dd>"
when RDoc::Markup::ListBase::NOTE then
"</td></tr>"
else
raise "Invalid list type"
end
end
end

331
lib/rdoc/markup/to_latex.rb Normal file
View file

@ -0,0 +1,331 @@
require 'rdoc/markup/fragments'
require 'rdoc/markup/inline'
require 'cgi'
##
# Convert SimpleMarkup to basic LaTeX report format.
class RDoc::Markup::ToLaTeX
BS = "\020" # \
OB = "\021" # {
CB = "\022" # }
DL = "\023" # Dollar
BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
HAT = "#{BS}symbol#{OB}94#{CB}"
BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
TILDE = "#{DL}#{BS}sim#{DL}"
LESSTHAN = "#{DL}<#{DL}"
GREATERTHAN = "#{DL}>#{DL}"
def self.l(str)
str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
end
def l(arg)
RDoc::Markup::ToLaTeX.l(arg)
end
LIST_TYPE_TO_LATEX = {
ListBase::BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
ListBase::NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
ListBase::UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
ListBase::LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
ListBase::LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
ListBase::NOTE => [
l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
l("\\end{tabularx}") ],
}
InlineTag = Struct.new(:bit, :on, :off)
def initialize
init_tags
@list_depth = 0
@prev_list_types = []
end
##
# Set up the standard mapping of attributes to LaTeX
def init_tags
@attr_tags = [
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
]
end
##
# Escape a LaTeX string
def escape(str)
$stderr.print "FE: ", str
s = str.
sub(/\s+$/, '').
gsub(/([_\${}&%#])/, "#{BS}\\1").
gsub(/\\/, BACKSLASH).
gsub(/\^/, HAT).
gsub(/~/, TILDE).
gsub(/</, LESSTHAN).
gsub(/>/, GREATERTHAN).
gsub(/,,/, ",{},").
gsub(/\`/, BACKQUOTE)
$stderr.print "-> ", s, "\n"
s
end
##
# Add a new set of LaTeX tags for an attribute. We allow
# separate start and end tags for flexibility
def add_tag(name, start, stop)
@attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
end
##
# Here's the client side of the visitor pattern
def start_accepting
@res = ""
@in_list_entry = []
end
def end_accepting
@res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
end
def accept_paragraph(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt)))
@res << "\n"
end
def accept_verbatim(am, fragment)
@res << "\n\\begin{code}\n"
@res << fragment.txt.sub(/[\n\s]+\Z/, '')
@res << "\n\\end{code}\n\n"
end
def accept_rule(am, fragment)
size = fragment.param
size = 10 if size > 10
@res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
end
def accept_list_start(am, fragment)
@res << list_name(fragment.type, true) << "\n"
@in_list_entry.push false
end
def accept_list_end(am, fragment)
if tag = @in_list_entry.pop
@res << tag << "\n"
end
@res << list_name(fragment.type, false) << "\n"
end
def accept_list_item(am, fragment)
if tag = @in_list_entry.last
@res << tag << "\n"
end
@res << list_item_start(am, fragment)
@res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
@in_list_entry[-1] = list_end_for(fragment.type)
end
def accept_blank_line(am, fragment)
# @res << "\n"
end
def accept_heading(am, fragment)
@res << convert_heading(fragment.head_level, am.flow(fragment.txt))
end
##
# This is a higher speed (if messier) version of wrap
def wrap(txt, line_len = 76)
res = ""
sp = 0
ep = txt.length
while sp < ep
# scan back for a space
p = sp + line_len - 1
if p >= ep
p = ep
else
while p > sp and txt[p] != ?\s
p -= 1
end
if p <= sp
p = sp + line_len
while p < ep and txt[p] != ?\s
p += 1
end
end
end
res << txt[sp...p] << "\n"
sp = p
sp += 1 while sp < ep and txt[sp] == ?\s
end
res
end
private
def on_tags(res, item)
attr_mask = item.turn_on
return if attr_mask.zero?
@attr_tags.each do |tag|
if attr_mask & tag.bit != 0
res << tag.on
end
end
end
def off_tags(res, item)
attr_mask = item.turn_off
return if attr_mask.zero?
@attr_tags.reverse_each do |tag|
if attr_mask & tag.bit != 0
res << tag.off
end
end
end
def convert_flow(flow)
res = ""
flow.each do |item|
case item
when String
$stderr.puts "Converting '#{item}'"
res << convert_string(item)
when AttrChanger
off_tags(res, item)
on_tags(res, item)
when Special
res << convert_special(item)
else
raise "Unknown flow element: #{item.inspect}"
end
end
res
end
##
# some of these patterns are taken from SmartyPants...
def convert_string(item)
escape(item).
# convert ... to elipsis (and make sure .... becomes .<elipsis>)
gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
# convert single closing quote
gsub(%r{([^ \t\r\n\[\{\(])\'}) { "#$1'" }.
gsub(%r{\'(?=\W|s\b)}) { "'" }.
# convert single opening quote
gsub(/'/, '`').
# convert double closing quote
gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}) { "#$1''" }.
# convert double opening quote
gsub(/"/, "``").
# convert copyright
gsub(/\(c\)/, '\copyright{}')
end
def convert_special(special)
handled = false
Attribute.each_name_of(special.type) do |name|
method_name = "handle_special_#{name}"
if self.respond_to? method_name
special.text = send(method_name, special)
handled = true
end
end
raise "Unhandled special: #{special}" unless handled
special.text
end
def convert_heading(level, flow)
res =
case level
when 1 then "\\chapter{"
when 2 then "\\section{"
when 3 then "\\subsection{"
when 4 then "\\subsubsection{"
else "\\paragraph{"
end +
convert_flow(flow) +
"}\n"
end
def list_name(list_type, is_open_tag)
tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
if tags[2] # enumerate
if is_open_tag
@list_depth += 1
if @prev_list_types[@list_depth] != tags[2]
case @list_depth
when 1
roman = "i"
when 2
roman = "ii"
when 3
roman = "iii"
when 4
roman = "iv"
else
raise("Too deep list: level #{@list_depth}")
end
@prev_list_types[@list_depth] = tags[2]
return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
end
else
@list_depth -= 1
end
end
tags[ is_open_tag ? 0 : 1]
end
def list_item_start(am, fragment)
case fragment.type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
ListBase::LOWERALPHA
"\\item "
when ListBase::LABELED
"\\item[" + convert_flow(am.flow(fragment.param)) + "] "
when ListBase::NOTE
convert_flow(am.flow(fragment.param)) + " & "
else
raise "Invalid list type"
end
end
def list_end_for(fragment_type)
case fragment_type
when ListBase::BULLET, ListBase::NUMBER, ListBase::UPPERALPHA,
ListBase::LOWERALPHA, ListBase::LABELED
""
when ListBase::NOTE
"\\\\\n"
else
raise "Invalid list type"
end
end
end
d