rdoc 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rdoc might be problematic. Click here for more details.

Files changed (62) hide show
  1. data/History.txt +13 -0
  2. data/Manifest.txt +61 -0
  3. data/README.txt +34 -0
  4. data/Rakefile +10 -0
  5. data/bin/rdoc +22 -0
  6. data/bin/ri +6 -0
  7. data/lib/rdoc.rb +277 -0
  8. data/lib/rdoc/code_objects.rb +776 -0
  9. data/lib/rdoc/diagram.rb +338 -0
  10. data/lib/rdoc/dot.rb +249 -0
  11. data/lib/rdoc/generator.rb +1048 -0
  12. data/lib/rdoc/generator/chm.rb +113 -0
  13. data/lib/rdoc/generator/chm/chm.rb +98 -0
  14. data/lib/rdoc/generator/html.rb +370 -0
  15. data/lib/rdoc/generator/html/hefss.rb +414 -0
  16. data/lib/rdoc/generator/html/html.rb +704 -0
  17. data/lib/rdoc/generator/html/kilmer.rb +418 -0
  18. data/lib/rdoc/generator/html/one_page_html.rb +121 -0
  19. data/lib/rdoc/generator/ri.rb +229 -0
  20. data/lib/rdoc/generator/xml.rb +120 -0
  21. data/lib/rdoc/generator/xml/rdf.rb +113 -0
  22. data/lib/rdoc/generator/xml/xml.rb +111 -0
  23. data/lib/rdoc/markup.rb +473 -0
  24. data/lib/rdoc/markup/attribute_manager.rb +274 -0
  25. data/lib/rdoc/markup/formatter.rb +14 -0
  26. data/lib/rdoc/markup/fragments.rb +337 -0
  27. data/lib/rdoc/markup/inline.rb +101 -0
  28. data/lib/rdoc/markup/lines.rb +152 -0
  29. data/lib/rdoc/markup/preprocess.rb +71 -0
  30. data/lib/rdoc/markup/to_flow.rb +185 -0
  31. data/lib/rdoc/markup/to_html.rb +353 -0
  32. data/lib/rdoc/markup/to_html_crossref.rb +86 -0
  33. data/lib/rdoc/markup/to_latex.rb +328 -0
  34. data/lib/rdoc/markup/to_test.rb +50 -0
  35. data/lib/rdoc/options.rb +616 -0
  36. data/lib/rdoc/parsers/parse_c.rb +775 -0
  37. data/lib/rdoc/parsers/parse_f95.rb +1841 -0
  38. data/lib/rdoc/parsers/parse_rb.rb +2584 -0
  39. data/lib/rdoc/parsers/parse_simple.rb +40 -0
  40. data/lib/rdoc/parsers/parserfactory.rb +99 -0
  41. data/lib/rdoc/rdoc.rb +277 -0
  42. data/lib/rdoc/ri.rb +4 -0
  43. data/lib/rdoc/ri/cache.rb +188 -0
  44. data/lib/rdoc/ri/descriptions.rb +150 -0
  45. data/lib/rdoc/ri/display.rb +274 -0
  46. data/lib/rdoc/ri/driver.rb +452 -0
  47. data/lib/rdoc/ri/formatter.rb +616 -0
  48. data/lib/rdoc/ri/paths.rb +102 -0
  49. data/lib/rdoc/ri/reader.rb +106 -0
  50. data/lib/rdoc/ri/util.rb +81 -0
  51. data/lib/rdoc/ri/writer.rb +68 -0
  52. data/lib/rdoc/stats.rb +25 -0
  53. data/lib/rdoc/template.rb +64 -0
  54. data/lib/rdoc/tokenstream.rb +33 -0
  55. data/test/test_rdoc_c_parser.rb +261 -0
  56. data/test/test_rdoc_markup.rb +613 -0
  57. data/test/test_rdoc_markup_attribute_manager.rb +224 -0
  58. data/test/test_rdoc_ri_attribute_formatter.rb +42 -0
  59. data/test/test_rdoc_ri_default_display.rb +295 -0
  60. data/test/test_rdoc_ri_formatter.rb +318 -0
  61. data/test/test_rdoc_ri_overstrike_formatter.rb +69 -0
  62. metadata +134 -0
@@ -0,0 +1,353 @@
1
+ require 'rdoc/markup/formatter'
2
+ require 'rdoc/markup/fragments'
3
+ require 'rdoc/markup/inline'
4
+
5
+ require 'cgi'
6
+
7
+ class RDoc::Markup::ToHtml < RDoc::Markup::Formatter
8
+
9
+ LIST_TYPE_TO_HTML = {
10
+ :BULLET => %w[<ul> </ul>],
11
+ :NUMBER => %w[<ol> </ol>],
12
+ :UPPERALPHA => %w[<ol> </ol>],
13
+ :LOWERALPHA => %w[<ol> </ol>],
14
+ :LABELED => %w[<dl> </dl>],
15
+ :NOTE => %w[<table> </table>],
16
+ }
17
+
18
+ InlineTag = Struct.new(:bit, :on, :off)
19
+
20
+ def initialize
21
+ super
22
+
23
+ # external hyperlinks
24
+ @markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
25
+
26
+ # and links of the form <text>[<url>]
27
+ @markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
28
+
29
+ init_tags
30
+ end
31
+
32
+ ##
33
+ # Generate a hyperlink for url, labeled with text. Handle the
34
+ # special cases for img: and link: described under handle_special_HYPEDLINK
35
+
36
+ def gen_url(url, text)
37
+ if url =~ /([A-Za-z]+):(.*)/ then
38
+ type = $1
39
+ path = $2
40
+ else
41
+ type = "http"
42
+ path = url
43
+ url = "http://#{url}"
44
+ end
45
+
46
+ if type == "link" then
47
+ url = if path[0, 1] == '#' then # is this meaningful?
48
+ path
49
+ else
50
+ HTML.gen_url @from_path, path
51
+ end
52
+ end
53
+
54
+ if (type == "http" or type == "link") and
55
+ url =~ /\.(gif|png|jpg|jpeg|bmp)$/ then
56
+ "<img src=\"#{url}\" />"
57
+ else
58
+ "<a href=\"#{url}\">#{text.sub(%r{^#{type}:/*}, '')}</a>"
59
+ end
60
+ end
61
+
62
+ ##
63
+ # And we're invoked with a potential external hyperlink mailto:
64
+ # just gets inserted. http: links are checked to see if they
65
+ # reference an image. If so, that image gets inserted using an
66
+ # <img> tag. Otherwise a conventional <a href> is used. We also
67
+ # support a special type of hyperlink, link:, which is a reference
68
+ # to a local file whose path is relative to the --op directory.
69
+
70
+ def handle_special_HYPERLINK(special)
71
+ url = special.text
72
+ gen_url url, url
73
+ end
74
+
75
+ ##
76
+ # Here's a hypedlink where the label is different to the URL
77
+ # <label>[url] or {long label}[url]
78
+
79
+ def handle_special_TIDYLINK(special)
80
+ text = special.text
81
+
82
+ return text unless text =~ /\{(.*?)\}\[(.*?)\]/ or text =~ /(\S+)\[(.*?)\]/
83
+
84
+ label = $1
85
+ url = $2
86
+ gen_url url, label
87
+ end
88
+
89
+ ##
90
+ # Set up the standard mapping of attributes to HTML tags
91
+
92
+ def init_tags
93
+ @attr_tags = [
94
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), "<b>", "</b>"),
95
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), "<tt>", "</tt>"),
96
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), "<em>", "</em>"),
97
+ ]
98
+ end
99
+
100
+ ##
101
+ # Add a new set of HTML tags for an attribute. We allow separate start and
102
+ # end tags for flexibility.
103
+
104
+ def add_tag(name, start, stop)
105
+ @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
106
+ end
107
+
108
+ ##
109
+ # Given an HTML tag, decorate it with class information and the like if
110
+ # required. This is a no-op in the base class, but is overridden in HTML
111
+ # output classes that implement style sheets.
112
+
113
+ def annotate(tag)
114
+ tag
115
+ end
116
+
117
+ ##
118
+ # Here's the client side of the visitor pattern
119
+
120
+ def start_accepting
121
+ @res = ""
122
+ @in_list_entry = []
123
+ end
124
+
125
+ def end_accepting
126
+ @res
127
+ end
128
+
129
+ def accept_paragraph(am, fragment)
130
+ @res << annotate("<p>") + "\n"
131
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
132
+ @res << annotate("</p>") + "\n"
133
+ end
134
+
135
+ def accept_verbatim(am, fragment)
136
+ @res << annotate("<pre>") + "\n"
137
+ @res << CGI.escapeHTML(fragment.txt)
138
+ @res << annotate("</pre>") << "\n"
139
+ end
140
+
141
+ def accept_rule(am, fragment)
142
+ size = fragment.param
143
+ size = 10 if size > 10
144
+ @res << "<hr size=\"#{size}\"></hr>"
145
+ end
146
+
147
+ def accept_list_start(am, fragment)
148
+ @res << html_list_name(fragment.type, true) << "\n"
149
+ @in_list_entry.push false
150
+ end
151
+
152
+ def accept_list_end(am, fragment)
153
+ if tag = @in_list_entry.pop
154
+ @res << annotate(tag) << "\n"
155
+ end
156
+ @res << html_list_name(fragment.type, false) << "\n"
157
+ end
158
+
159
+ def accept_list_item(am, fragment)
160
+ if tag = @in_list_entry.last
161
+ @res << annotate(tag) << "\n"
162
+ end
163
+
164
+ @res << list_item_start(am, fragment)
165
+
166
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
167
+
168
+ @in_list_entry[-1] = list_end_for(fragment.type)
169
+ end
170
+
171
+ def accept_blank_line(am, fragment)
172
+ # @res << annotate("<p />") << "\n"
173
+ end
174
+
175
+ def accept_heading(am, fragment)
176
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
177
+ end
178
+
179
+ ##
180
+ # This is a higher speed (if messier) version of wrap
181
+
182
+ def wrap(txt, line_len = 76)
183
+ res = ""
184
+ sp = 0
185
+ ep = txt.length
186
+ while sp < ep
187
+ # scan back for a space
188
+ p = sp + line_len - 1
189
+ if p >= ep
190
+ p = ep
191
+ else
192
+ while p > sp and txt[p] != ?\s
193
+ p -= 1
194
+ end
195
+ if p <= sp
196
+ p = sp + line_len
197
+ while p < ep and txt[p] != ?\s
198
+ p += 1
199
+ end
200
+ end
201
+ end
202
+ res << txt[sp...p] << "\n"
203
+ sp = p
204
+ sp += 1 while sp < ep and txt[sp] == ?\s
205
+ end
206
+ res
207
+ end
208
+
209
+ private
210
+
211
+ def on_tags(res, item)
212
+ attr_mask = item.turn_on
213
+ return if attr_mask.zero?
214
+
215
+ @attr_tags.each do |tag|
216
+ if attr_mask & tag.bit != 0
217
+ res << annotate(tag.on)
218
+ end
219
+ end
220
+ end
221
+
222
+ def off_tags(res, item)
223
+ attr_mask = item.turn_off
224
+ return if attr_mask.zero?
225
+
226
+ @attr_tags.reverse_each do |tag|
227
+ if attr_mask & tag.bit != 0
228
+ res << annotate(tag.off)
229
+ end
230
+ end
231
+ end
232
+
233
+ def convert_flow(flow)
234
+ res = ""
235
+
236
+ flow.each do |item|
237
+ case item
238
+ when String
239
+ res << convert_string(item)
240
+ when RDoc::Markup::AttrChanger
241
+ off_tags(res, item)
242
+ on_tags(res, item)
243
+ when RDoc::Markup::Special
244
+ res << convert_special(item)
245
+ else
246
+ raise "Unknown flow element: #{item.inspect}"
247
+ end
248
+ end
249
+
250
+ res
251
+ end
252
+
253
+ ##
254
+ # some of these patterns are taken from SmartyPants...
255
+
256
+ def convert_string(item)
257
+ CGI.escapeHTML(item).
258
+
259
+ # convert -- to em-dash, (-- to en-dash)
260
+ gsub(/---?/, '&#8212;'). #gsub(/--/, '&#8211;').
261
+
262
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
263
+ gsub(/\.\.\.\./, '.&#8230;').gsub(/\.\.\./, '&#8230;').
264
+
265
+ # convert single closing quote
266
+ gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1&#8217;').
267
+ gsub(%r{\'(?=\W|s\b)}, '&#8217;').
268
+
269
+ # convert single opening quote
270
+ gsub(/'/, '&#8216;').
271
+
272
+ # convert double closing quote
273
+ gsub(%r{([^ \t\r\n\[\{\(])\'(?=\W)}, '\1&#8221;').
274
+
275
+ # convert double opening quote
276
+ gsub(/'/, '&#8220;').
277
+
278
+ # convert copyright
279
+ gsub(/\(c\)/, '&#169;').
280
+
281
+ # convert and registered trademark
282
+ gsub(/\(r\)/, '&#174;')
283
+
284
+ end
285
+
286
+ def convert_special(special)
287
+ handled = false
288
+ RDoc::Markup::Attribute.each_name_of(special.type) do |name|
289
+ method_name = "handle_special_#{name}"
290
+ if self.respond_to? method_name
291
+ special.text = send(method_name, special)
292
+ handled = true
293
+ end
294
+ end
295
+ raise "Unhandled special: #{special}" unless handled
296
+ special.text
297
+ end
298
+
299
+ def convert_heading(level, flow)
300
+ res =
301
+ annotate("<h#{level}>") +
302
+ convert_flow(flow) +
303
+ annotate("</h#{level}>\n")
304
+ end
305
+
306
+ def html_list_name(list_type, is_open_tag)
307
+ tags = LIST_TYPE_TO_HTML[list_type] || raise("Invalid list type: #{list_type.inspect}")
308
+ annotate(tags[ is_open_tag ? 0 : 1])
309
+ end
310
+
311
+ def list_item_start(am, fragment)
312
+ case fragment.type
313
+ when :BULLET, :NUMBER then
314
+ annotate("<li>")
315
+
316
+ when :UPPERALPHA then
317
+ annotate("<li type=\"A\">")
318
+
319
+ when :LOWERALPHA then
320
+ annotate("<li type=\"a\">")
321
+
322
+ when :LABELED then
323
+ annotate("<dt>") +
324
+ convert_flow(am.flow(fragment.param)) +
325
+ annotate("</dt>") +
326
+ annotate("<dd>")
327
+
328
+ when :NOTE then
329
+ annotate("<tr>") +
330
+ annotate("<td valign=\"top\">") +
331
+ convert_flow(am.flow(fragment.param)) +
332
+ annotate("</td>") +
333
+ annotate("<td>")
334
+ else
335
+ raise "Invalid list type"
336
+ end
337
+ end
338
+
339
+ def list_end_for(fragment_type)
340
+ case fragment_type
341
+ when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
342
+ "</li>"
343
+ when :LABELED then
344
+ "</dd>"
345
+ when :NOTE then
346
+ "</td></tr>"
347
+ else
348
+ raise "Invalid list type"
349
+ end
350
+ end
351
+
352
+ end
353
+
@@ -0,0 +1,86 @@
1
+ require 'rdoc/markup/to_html'
2
+
3
+ ##
4
+ # Subclass of the RDoc::Markup::ToHtml class that supports looking up words in
5
+ # the AllReferences list. Those that are found (like AllReferences in this
6
+ # comment) will be hyperlinked
7
+
8
+ class RDoc::Markup::ToHtmlCrossref < RDoc::Markup::ToHtml
9
+
10
+ attr_accessor :context
11
+
12
+ ##
13
+ # We need to record the html path of our caller so we can generate
14
+ # correct relative paths for any hyperlinks that we find
15
+
16
+ def initialize(from_path, context, show_hash)
17
+ super()
18
+
19
+ # class names, variable names, or instance variables
20
+ @markup.add_special(/(
21
+ # A::B.meth(**) (for operator in Fortran95)
22
+ \w+(::\w+)*[.\#]\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?
23
+ # meth(**) (for operator in Fortran95)
24
+ | \#\w+(\([.\w\*\/\+\-\=\<\>]+\))?
25
+ | \b([A-Z]\w*(::\w+)*[.\#]\w+) # A::B.meth
26
+ | \b([A-Z]\w+(::\w+)*) # A::B
27
+ | \#\w+[!?=]? # #meth_name
28
+ | \\?\b\w+([_\/\.]+\w+)*[!?=]? # meth_name
29
+ )/x,
30
+ :CROSSREF)
31
+
32
+ @from_path = from_path
33
+ @context = context
34
+ @show_hash = show_hash
35
+
36
+ @seen = {}
37
+ end
38
+
39
+ ##
40
+ # We're invoked when any text matches the CROSSREF pattern
41
+ # (defined in MarkUp). If we fine the corresponding reference,
42
+ # generate a hyperlink. If the name we're looking for contains
43
+ # no punctuation, we look for it up the module/class chain. For
44
+ # example, HyperlinkHtml is found, even without the Generator::
45
+ # prefix, because we look for it in module Generator first.
46
+
47
+ def handle_special_CROSSREF(special)
48
+ name = special.text
49
+
50
+ return @seen[name] if @seen.include? name
51
+
52
+ if name[0,1] == '#' then
53
+ lookup = name[1..-1]
54
+ name = lookup unless @show_hash
55
+ else
56
+ lookup = name
57
+ end
58
+
59
+ # Find class, module, or method in class or module.
60
+ if /([A-Z]\w*)[.\#](\w+[!?=]?)/ =~ lookup then
61
+ container = $1
62
+ method = $2
63
+ ref = @context.find_symbol container, method
64
+ elsif /([A-Za-z]\w*)[.\#](\w+(\([\.\w+\*\/\+\-\=\<\>]+\))?)/ =~ lookup then
65
+ container = $1
66
+ method = $2
67
+ ref = @context.find_symbol container, method
68
+ else
69
+ ref = @context.find_symbol lookup
70
+ end
71
+
72
+ out = if lookup =~ /^\\/ then
73
+ $'
74
+ elsif ref and ref.document_self then
75
+ "<a href=\"#{ref.as_href(@from_path)}\">#{name}</a>"
76
+ else
77
+ name
78
+ end
79
+
80
+ @seen[name] = out
81
+
82
+ out
83
+ end
84
+
85
+ end
86
+
@@ -0,0 +1,328 @@
1
+ require 'rdoc/markup/formatter'
2
+ require 'rdoc/markup/fragments'
3
+ require 'rdoc/markup/inline'
4
+
5
+ require 'cgi'
6
+
7
+ ##
8
+ # Convert SimpleMarkup to basic LaTeX report format.
9
+
10
+ class RDoc::Markup::ToLaTeX < RDoc::Markup::Formatter
11
+
12
+ BS = "\020" # \
13
+ OB = "\021" # {
14
+ CB = "\022" # }
15
+ DL = "\023" # Dollar
16
+
17
+ BACKSLASH = "#{BS}symbol#{OB}92#{CB}"
18
+ HAT = "#{BS}symbol#{OB}94#{CB}"
19
+ BACKQUOTE = "#{BS}symbol#{OB}0#{CB}"
20
+ TILDE = "#{DL}#{BS}sim#{DL}"
21
+ LESSTHAN = "#{DL}<#{DL}"
22
+ GREATERTHAN = "#{DL}>#{DL}"
23
+
24
+ def self.l(str)
25
+ str.tr('\\', BS).tr('{', OB).tr('}', CB).tr('$', DL)
26
+ end
27
+
28
+ def l(arg)
29
+ RDoc::Markup::ToLaTeX.l(arg)
30
+ end
31
+
32
+ LIST_TYPE_TO_LATEX = {
33
+ :BULLET => [ l("\\begin{itemize}"), l("\\end{itemize}") ],
34
+ :NUMBER => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\arabic" ],
35
+ :UPPERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\Alph" ],
36
+ :LOWERALPHA => [ l("\\begin{enumerate}"), l("\\end{enumerate}"), "\\alph" ],
37
+ :LABELED => [ l("\\begin{description}"), l("\\end{description}") ],
38
+ :NOTE => [
39
+ l("\\begin{tabularx}{\\linewidth}{@{} l X @{}}"),
40
+ l("\\end{tabularx}") ],
41
+ }
42
+
43
+ InlineTag = Struct.new(:bit, :on, :off)
44
+
45
+ def initialize
46
+ init_tags
47
+ @list_depth = 0
48
+ @prev_list_types = []
49
+ end
50
+
51
+ ##
52
+ # Set up the standard mapping of attributes to LaTeX
53
+
54
+ def init_tags
55
+ @attr_tags = [
56
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:BOLD), l("\\textbf{"), l("}")),
57
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:TT), l("\\texttt{"), l("}")),
58
+ InlineTag.new(RDoc::Markup::Attribute.bitmap_for(:EM), l("\\emph{"), l("}")),
59
+ ]
60
+ end
61
+
62
+ ##
63
+ # Escape a LaTeX string
64
+
65
+ def escape(str)
66
+ $stderr.print "FE: ", str if $DEBUG_RDOC
67
+ s = str.
68
+ sub(/\s+$/, '').
69
+ gsub(/([_\${}&%#])/, "#{BS}\\1").
70
+ gsub(/\\/, BACKSLASH).
71
+ gsub(/\^/, HAT).
72
+ gsub(/~/, TILDE).
73
+ gsub(/</, LESSTHAN).
74
+ gsub(/>/, GREATERTHAN).
75
+ gsub(/,,/, ",{},").
76
+ gsub(/\`/, BACKQUOTE)
77
+ $stderr.print "-> ", s, "\n" if $DEBUG_RDOC
78
+ s
79
+ end
80
+
81
+ ##
82
+ # Add a new set of LaTeX tags for an attribute. We allow
83
+ # separate start and end tags for flexibility
84
+
85
+ def add_tag(name, start, stop)
86
+ @attr_tags << InlineTag.new(RDoc::Markup::Attribute.bitmap_for(name), start, stop)
87
+ end
88
+
89
+ ##
90
+ # Here's the client side of the visitor pattern
91
+
92
+ def start_accepting
93
+ @res = ""
94
+ @in_list_entry = []
95
+ end
96
+
97
+ def end_accepting
98
+ @res.tr(BS, '\\').tr(OB, '{').tr(CB, '}').tr(DL, '$')
99
+ end
100
+
101
+ def accept_paragraph(am, fragment)
102
+ @res << wrap(convert_flow(am.flow(fragment.txt)))
103
+ @res << "\n"
104
+ end
105
+
106
+ def accept_verbatim(am, fragment)
107
+ @res << "\n\\begin{code}\n"
108
+ @res << fragment.txt.sub(/[\n\s]+\Z/, '')
109
+ @res << "\n\\end{code}\n\n"
110
+ end
111
+
112
+ def accept_rule(am, fragment)
113
+ size = fragment.param
114
+ size = 10 if size > 10
115
+ @res << "\n\n\\rule{\\linewidth}{#{size}pt}\n\n"
116
+ end
117
+
118
+ def accept_list_start(am, fragment)
119
+ @res << list_name(fragment.type, true) << "\n"
120
+ @in_list_entry.push false
121
+ end
122
+
123
+ def accept_list_end(am, fragment)
124
+ if tag = @in_list_entry.pop
125
+ @res << tag << "\n"
126
+ end
127
+ @res << list_name(fragment.type, false) << "\n"
128
+ end
129
+
130
+ def accept_list_item(am, fragment)
131
+ if tag = @in_list_entry.last
132
+ @res << tag << "\n"
133
+ end
134
+ @res << list_item_start(am, fragment)
135
+ @res << wrap(convert_flow(am.flow(fragment.txt))) << "\n"
136
+ @in_list_entry[-1] = list_end_for(fragment.type)
137
+ end
138
+
139
+ def accept_blank_line(am, fragment)
140
+ # @res << "\n"
141
+ end
142
+
143
+ def accept_heading(am, fragment)
144
+ @res << convert_heading(fragment.head_level, am.flow(fragment.txt))
145
+ end
146
+
147
+ ##
148
+ # This is a higher speed (if messier) version of wrap
149
+
150
+ def wrap(txt, line_len = 76)
151
+ res = ""
152
+ sp = 0
153
+ ep = txt.length
154
+ while sp < ep
155
+ # scan back for a space
156
+ p = sp + line_len - 1
157
+ if p >= ep
158
+ p = ep
159
+ else
160
+ while p > sp and txt[p] != ?\s
161
+ p -= 1
162
+ end
163
+ if p <= sp
164
+ p = sp + line_len
165
+ while p < ep and txt[p] != ?\s
166
+ p += 1
167
+ end
168
+ end
169
+ end
170
+ res << txt[sp...p] << "\n"
171
+ sp = p
172
+ sp += 1 while sp < ep and txt[sp] == ?\s
173
+ end
174
+ res
175
+ end
176
+
177
+ private
178
+
179
+ def on_tags(res, item)
180
+ attr_mask = item.turn_on
181
+ return if attr_mask.zero?
182
+
183
+ @attr_tags.each do |tag|
184
+ if attr_mask & tag.bit != 0
185
+ res << tag.on
186
+ end
187
+ end
188
+ end
189
+
190
+ def off_tags(res, item)
191
+ attr_mask = item.turn_off
192
+ return if attr_mask.zero?
193
+
194
+ @attr_tags.reverse_each do |tag|
195
+ if attr_mask & tag.bit != 0
196
+ res << tag.off
197
+ end
198
+ end
199
+ end
200
+
201
+ def convert_flow(flow)
202
+ res = ""
203
+ flow.each do |item|
204
+ case item
205
+ when String
206
+ $stderr.puts "Converting '#{item}'" if $DEBUG_RDOC
207
+ res << convert_string(item)
208
+ when AttrChanger
209
+ off_tags(res, item)
210
+ on_tags(res, item)
211
+ when Special
212
+ res << convert_special(item)
213
+ else
214
+ raise "Unknown flow element: #{item.inspect}"
215
+ end
216
+ end
217
+ res
218
+ end
219
+
220
+ ##
221
+ # some of these patterns are taken from SmartyPants...
222
+
223
+ def convert_string(item)
224
+ escape(item).
225
+
226
+ # convert ... to elipsis (and make sure .... becomes .<elipsis>)
227
+ gsub(/\.\.\.\./, '.\ldots{}').gsub(/\.\.\./, '\ldots{}').
228
+
229
+ # convert single closing quote
230
+ gsub(%r{([^ \t\r\n\[\{\(])\'}, '\1\'').
231
+ gsub(%r{\'(?=\W|s\b)}, "'" ).
232
+
233
+ # convert single opening quote
234
+ gsub(/'/, '`').
235
+
236
+ # convert double closing quote
237
+ gsub(%r{([^ \t\r\n\[\{\(])\"(?=\W)}, "\\1''").
238
+
239
+ # convert double opening quote
240
+ gsub(/"/, "``").
241
+
242
+ # convert copyright
243
+ gsub(/\(c\)/, '\copyright{}')
244
+
245
+ end
246
+
247
+ def convert_special(special)
248
+ handled = false
249
+ Attribute.each_name_of(special.type) do |name|
250
+ method_name = "handle_special_#{name}"
251
+ if self.respond_to? method_name
252
+ special.text = send(method_name, special)
253
+ handled = true
254
+ end
255
+ end
256
+ raise "Unhandled special: #{special}" unless handled
257
+ special.text
258
+ end
259
+
260
+ def convert_heading(level, flow)
261
+ res =
262
+ case level
263
+ when 1 then "\\chapter{"
264
+ when 2 then "\\section{"
265
+ when 3 then "\\subsection{"
266
+ when 4 then "\\subsubsection{"
267
+ else "\\paragraph{"
268
+ end +
269
+ convert_flow(flow) +
270
+ "}\n"
271
+ end
272
+
273
+ def list_name(list_type, is_open_tag)
274
+ tags = LIST_TYPE_TO_LATEX[list_type] || raise("Invalid list type: #{list_type.inspect}")
275
+ if tags[2] # enumerate
276
+ if is_open_tag
277
+ @list_depth += 1
278
+ if @prev_list_types[@list_depth] != tags[2]
279
+ case @list_depth
280
+ when 1
281
+ roman = "i"
282
+ when 2
283
+ roman = "ii"
284
+ when 3
285
+ roman = "iii"
286
+ when 4
287
+ roman = "iv"
288
+ else
289
+ raise("Too deep list: level #{@list_depth}")
290
+ end
291
+ @prev_list_types[@list_depth] = tags[2]
292
+ return l("\\renewcommand{\\labelenum#{roman}}{#{tags[2]}{enum#{roman}}}") + "\n" + tags[0]
293
+ end
294
+ else
295
+ @list_depth -= 1
296
+ end
297
+ end
298
+ tags[ is_open_tag ? 0 : 1]
299
+ end
300
+
301
+ def list_item_start(am, fragment)
302
+ case fragment.type
303
+ when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA then
304
+ "\\item "
305
+
306
+ when :LABELED then
307
+ "\\item[" + convert_flow(am.flow(fragment.param)) + "] "
308
+
309
+ when :NOTE then
310
+ convert_flow(am.flow(fragment.param)) + " & "
311
+ else
312
+ raise "Invalid list type"
313
+ end
314
+ end
315
+
316
+ def list_end_for(fragment_type)
317
+ case fragment_type
318
+ when :BULLET, :NUMBER, :UPPERALPHA, :LOWERALPHA, :LABELED then
319
+ ""
320
+ when :NOTE
321
+ "\\\\\n"
322
+ else
323
+ raise "Invalid list type"
324
+ end
325
+ end
326
+
327
+ end
328
+