iownbey-rdoc 2.0.1

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