RedCloth 4.1.0-universal-java

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

Potentially problematic release.


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

Files changed (59) hide show
  1. data/CHANGELOG +103 -0
  2. data/COPYING +18 -0
  3. data/Manifest +57 -0
  4. data/README +156 -0
  5. data/Rakefile +205 -0
  6. data/RedCloth.gemspec +141 -0
  7. data/bin/redcloth +28 -0
  8. data/ext/mingw-rbconfig.rb +176 -0
  9. data/ext/redcloth_scan/extconf.rb +9 -0
  10. data/ext/redcloth_scan/redcloth.h +164 -0
  11. data/ext/redcloth_scan/redcloth_attributes.c.rl +56 -0
  12. data/ext/redcloth_scan/redcloth_attributes.java.rl +96 -0
  13. data/ext/redcloth_scan/redcloth_attributes.rl +33 -0
  14. data/ext/redcloth_scan/redcloth_common.c.rl +18 -0
  15. data/ext/redcloth_scan/redcloth_common.java.rl +18 -0
  16. data/ext/redcloth_scan/redcloth_common.rl +111 -0
  17. data/ext/redcloth_scan/redcloth_inline.c.rl +159 -0
  18. data/ext/redcloth_scan/redcloth_inline.java.rl +108 -0
  19. data/ext/redcloth_scan/redcloth_inline.rl +157 -0
  20. data/ext/redcloth_scan/redcloth_scan.c.rl +227 -0
  21. data/ext/redcloth_scan/redcloth_scan.java.rl +555 -0
  22. data/ext/redcloth_scan/redcloth_scan.rl +323 -0
  23. data/extras/ragel_profiler.rb +73 -0
  24. data/lib/case_sensitive_require/RedCloth.rb +6 -0
  25. data/lib/redcloth.rb +37 -0
  26. data/lib/redcloth/erb_extension.rb +27 -0
  27. data/lib/redcloth/formatters/base.rb +57 -0
  28. data/lib/redcloth/formatters/html.rb +349 -0
  29. data/lib/redcloth/formatters/latex.rb +249 -0
  30. data/lib/redcloth/formatters/latex_entities.yml +2414 -0
  31. data/lib/redcloth/textile_doc.rb +105 -0
  32. data/lib/redcloth/version.rb +28 -0
  33. data/lib/redcloth_scan.jar +0 -0
  34. data/setup.rb +1585 -0
  35. data/test/basic.yml +870 -0
  36. data/test/code.yml +229 -0
  37. data/test/definitions.yml +82 -0
  38. data/test/extra_whitespace.yml +64 -0
  39. data/test/filter_html.yml +177 -0
  40. data/test/filter_pba.yml +20 -0
  41. data/test/helper.rb +108 -0
  42. data/test/html.yml +305 -0
  43. data/test/images.yml +246 -0
  44. data/test/instiki.yml +38 -0
  45. data/test/links.yml +259 -0
  46. data/test/lists.yml +283 -0
  47. data/test/poignant.yml +89 -0
  48. data/test/sanitize_html.yml +42 -0
  49. data/test/table.yml +267 -0
  50. data/test/test_custom_tags.rb +46 -0
  51. data/test/test_erb.rb +13 -0
  52. data/test/test_extensions.rb +31 -0
  53. data/test/test_formatters.rb +24 -0
  54. data/test/test_parser.rb +73 -0
  55. data/test/test_restrictions.rb +41 -0
  56. data/test/textism.yml +480 -0
  57. data/test/threshold.yml +772 -0
  58. data/test/validate_fixtures.rb +73 -0
  59. metadata +139 -0
@@ -0,0 +1,57 @@
1
+ module RedCloth::Formatters
2
+ module Base
3
+
4
+ def pba(opts)
5
+ opts.delete(:style) if filter_styles
6
+ opts.delete(:class) if filter_classes
7
+ opts.delete(:id) if filter_ids
8
+
9
+ atts = ''
10
+ opts[:"text-align"] = opts.delete(:align)
11
+ opts[:style] += ';' if opts[:style] && (opts[:style][-1..-1] != ';')
12
+ [:float, :"text-align", :"vertical-align"].each do |a|
13
+ opts[:style] = "#{a}:#{opts[a]};#{opts[:style]}" if opts[a]
14
+ end
15
+ [:"padding-right", :"padding-left"].each do |a|
16
+ opts[:style] = "#{a}:#{opts[a]}em;#{opts[:style]}" if opts[a]
17
+ end
18
+ [:style, :class, :lang, :id, :colspan, :rowspan, :title, :start, :align].each do |a|
19
+ atts << " #{a}=\"#{ html_esc(opts[a].to_s, :html_escape_attributes) }\"" if opts[a]
20
+ end
21
+ atts
22
+ end
23
+
24
+ def ignore(opts)
25
+ opts[:text]
26
+ end
27
+ alias_method :notextile, :ignore
28
+
29
+ def redcloth_version(opts)
30
+ p(:text => "#{opts[:prefix]}#{RedCloth::VERSION}")
31
+ end
32
+
33
+ def inline_redcloth_version(opts)
34
+ RedCloth::VERSION::STRING
35
+ end
36
+
37
+ [:del_phrase, :sup_phrase, :sub_phrase, :span_phrase].each do |phrase_method|
38
+ method = phrase_method.to_s.split('_')[0]
39
+ define_method(phrase_method) do |opts|
40
+ "#{opts[:beginning_space]}#{self.send(method, opts)}"
41
+ end
42
+ end
43
+
44
+ def method_missing(method, opts)
45
+ opts[:text] || ""
46
+ end
47
+
48
+ def before_transform(text)
49
+
50
+ end
51
+
52
+ def after_transform(text)
53
+
54
+ end
55
+
56
+ end
57
+ end
@@ -0,0 +1,349 @@
1
+ module RedCloth::Formatters::HTML
2
+ include RedCloth::Formatters::Base
3
+
4
+ # escapement for regular HTML (not in PRE tag)
5
+ def escape(text)
6
+ html_esc(text)
7
+ end
8
+
9
+ # escapement for HTML in a PRE tag
10
+ def escape_pre(text)
11
+ html_esc(text, :html_escape_preformatted)
12
+ end
13
+
14
+ # escaping for HTML attributes
15
+ def escape_attribute(text)
16
+ html_esc(text, :html_escape_attributes)
17
+ end
18
+
19
+ def after_transform(text)
20
+ text.chomp!
21
+ end
22
+
23
+ [:h1, :h2, :h3, :h4, :h5, :h6, :p, :pre, :div].each do |m|
24
+ define_method(m) do |opts|
25
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
26
+ end
27
+ end
28
+
29
+ [:strong, :code, :em, :i, :b, :ins, :sup, :sub, :span, :cite].each do |m|
30
+ define_method(m) do |opts|
31
+ opts[:block] = true
32
+ "<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>"
33
+ end
34
+ end
35
+
36
+ def acronym(opts)
37
+ opts[:block] = true
38
+ "<acronym#{pba(opts)}>#{caps(:text => opts[:text])}</acronym>"
39
+ end
40
+
41
+ def caps(opts)
42
+ if no_span_caps
43
+ opts[:text]
44
+ else
45
+ opts[:class] = 'caps'
46
+ span(opts)
47
+ end
48
+ end
49
+
50
+ def del(opts)
51
+ opts[:block] = true
52
+ "<del#{pba(opts)}>#{opts[:text]}</del>"
53
+ end
54
+
55
+ [:ol, :ul].each do |m|
56
+ define_method("#{m}_open") do |opts|
57
+ opts[:block] = true
58
+ "#{"\n" if opts[:nest] > 1}#{"\t" * (opts[:nest] - 1)}<#{m}#{pba(opts)}>\n"
59
+ end
60
+ define_method("#{m}_close") do |opts|
61
+ "#{li_close}#{"\t" * (opts[:nest] - 1)}</#{m}>#{"\n" if opts[:nest] <= 1}"
62
+ end
63
+ end
64
+
65
+ def li_open(opts)
66
+ "#{li_close unless opts.delete(:first)}#{"\t" * opts[:nest]}<li#{pba(opts)}>#{opts[:text]}"
67
+ end
68
+
69
+ def li_close(opts=nil)
70
+ "</li>\n"
71
+ end
72
+
73
+ def dl_open(opts)
74
+ opts[:block] = true
75
+ "<dl#{pba(opts)}>\n"
76
+ end
77
+
78
+ def dl_close(opts=nil)
79
+ "</dl>\n"
80
+ end
81
+
82
+ [:dt, :dd].each do |m|
83
+ define_method(m) do |opts|
84
+ "\t<#{m}#{pba(opts)}>#{opts[:text]}</#{m}>\n"
85
+ end
86
+ end
87
+
88
+ def td(opts)
89
+ tdtype = opts[:th] ? 'th' : 'td'
90
+ "\t\t<#{tdtype}#{pba(opts)}>#{opts[:text]}</#{tdtype}>\n"
91
+ end
92
+
93
+ def tr_open(opts)
94
+ "\t<tr#{pba(opts)}>\n"
95
+ end
96
+
97
+ def tr_close(opts)
98
+ "\t</tr>\n"
99
+ end
100
+
101
+ def table_open(opts)
102
+ "<table#{pba(opts)}>\n"
103
+ end
104
+
105
+ def table_close(opts)
106
+ "</table>\n"
107
+ end
108
+
109
+ def bc_open(opts)
110
+ opts[:block] = true
111
+ "<pre#{pba(opts)}>"
112
+ end
113
+
114
+ def bc_close(opts)
115
+ "</pre>\n"
116
+ end
117
+
118
+ def bq_open(opts)
119
+ opts[:block] = true
120
+ cite = opts[:cite] ? " cite=\"#{ escape_attribute opts[:cite] }\"" : ''
121
+ "<blockquote#{cite}#{pba(opts)}>\n"
122
+ end
123
+
124
+ def bq_close(opts)
125
+ "</blockquote>\n"
126
+ end
127
+
128
+ LINK_TEXT_WITH_TITLE_RE = /
129
+ ([^"]+?) # $text
130
+ \s?
131
+ \(([^)]+?)\) # $title
132
+ $
133
+ /x
134
+ def link(opts)
135
+ if opts[:name] =~ LINK_TEXT_WITH_TITLE_RE
136
+ md = LINK_TEXT_WITH_TITLE_RE.match(opts[:name])
137
+ opts[:name] = md[1]
138
+ opts[:title] = md[2]
139
+ end
140
+ "<a href=\"#{escape_attribute opts[:href]}\"#{pba(opts)}>#{opts[:name]}</a>"
141
+ end
142
+
143
+ def image(opts)
144
+ opts.delete(:align)
145
+ opts[:alt] = opts[:title]
146
+ img = "<img src=\"#{escape_attribute opts[:src]}\"#{pba(opts)} alt=\"#{escape_attribute opts[:alt].to_s}\" />"
147
+ img = "<a href=\"#{escape_attribute opts[:href]}\">#{img}</a>" if opts[:href]
148
+ img
149
+ end
150
+
151
+ def footno(opts)
152
+ opts[:id] ||= opts[:text]
153
+ %Q{<sup class="footnote"><a href=\"#fn#{opts[:id]}\">#{opts[:text]}</a></sup>}
154
+ end
155
+
156
+ def fn(opts)
157
+ no = opts[:id]
158
+ opts[:id] = "fn#{no}"
159
+ opts[:class] = ["footnote", opts[:class]].compact.join(" ")
160
+ "<p#{pba(opts)}><sup>#{no}</sup> #{opts[:text]}</p>\n"
161
+ end
162
+
163
+ def snip(opts)
164
+ "<pre#{pba(opts)}><code>#{opts[:text]}</code></pre>\n"
165
+ end
166
+
167
+ def quote1(opts)
168
+ "&#8216;#{opts[:text]}&#8217;"
169
+ end
170
+
171
+ def quote2(opts)
172
+ "&#8220;#{opts[:text]}&#8221;"
173
+ end
174
+
175
+ def multi_paragraph_quote(opts)
176
+ "&#8220;#{opts[:text]}"
177
+ end
178
+
179
+ def ellipsis(opts)
180
+ "#{opts[:text]}&#8230;"
181
+ end
182
+
183
+ def emdash(opts)
184
+ "&#8212;"
185
+ end
186
+
187
+ def endash(opts)
188
+ " &#8211; "
189
+ end
190
+
191
+ def arrow(opts)
192
+ "&#8594;"
193
+ end
194
+
195
+ def dim(opts)
196
+ opts[:text].gsub!('x', '&#215;')
197
+ opts[:text].gsub!("'", '&#8242;')
198
+ opts[:text].gsub!('"', '&#8243;')
199
+ opts[:text]
200
+ end
201
+
202
+ def trademark(opts)
203
+ "&#8482;"
204
+ end
205
+
206
+ def registered(opts)
207
+ "&#174;"
208
+ end
209
+
210
+ def copyright(opts)
211
+ "&#169;"
212
+ end
213
+
214
+ def entity(opts)
215
+ "&#{opts[:text]};"
216
+ end
217
+
218
+ def amp(opts)
219
+ "&amp;"
220
+ end
221
+
222
+ def gt(opts)
223
+ "&gt;"
224
+ end
225
+
226
+ def lt(opts)
227
+ "&lt;"
228
+ end
229
+
230
+ def br(opts)
231
+ if hard_breaks == false
232
+ "\n"
233
+ else
234
+ "<br />\n"
235
+ end
236
+ end
237
+
238
+ def quot(opts)
239
+ "&quot;"
240
+ end
241
+
242
+ def squot(opts)
243
+ "&#8217;"
244
+ end
245
+
246
+ def apos(opts)
247
+ "&#39;"
248
+ end
249
+
250
+ def html(opts)
251
+ "#{opts[:text]}\n"
252
+ end
253
+
254
+ def html_block(opts)
255
+ inline_html(:text => "#{opts[:indent_before_start]}#{opts[:start_tag]}#{opts[:indent_after_start]}") +
256
+ "#{opts[:text]}" +
257
+ inline_html(:text => "#{opts[:indent_before_end]}#{opts[:end_tag]}#{opts[:indent_after_end]}")
258
+ end
259
+
260
+ def notextile(opts)
261
+ if filter_html
262
+ html_esc(opts[:text], :html_escape_preformatted)
263
+ else
264
+ opts[:text]
265
+ end
266
+ end
267
+
268
+ def inline_html(opts)
269
+ if filter_html
270
+ html_esc(opts[:text], :html_escape_preformatted)
271
+ else
272
+ "#{opts[:text]}" # nil-safe
273
+ end
274
+ end
275
+
276
+ def ignored_line(opts)
277
+ opts[:text] + "\n"
278
+ end
279
+
280
+ def before_transform(text)
281
+ clean_html(text) if sanitize_html
282
+ end
283
+
284
+ # HTML cleansing stuff
285
+ BASIC_TAGS = {
286
+ 'a' => ['href', 'title'],
287
+ 'img' => ['src', 'alt', 'title'],
288
+ 'br' => [],
289
+ 'i' => nil,
290
+ 'u' => nil,
291
+ 'b' => nil,
292
+ 'pre' => nil,
293
+ 'kbd' => nil,
294
+ 'code' => ['lang'],
295
+ 'cite' => nil,
296
+ 'strong' => nil,
297
+ 'em' => nil,
298
+ 'ins' => nil,
299
+ 'sup' => nil,
300
+ 'sub' => nil,
301
+ 'del' => nil,
302
+ 'table' => nil,
303
+ 'tr' => nil,
304
+ 'td' => ['colspan', 'rowspan'],
305
+ 'th' => nil,
306
+ 'ol' => ['start'],
307
+ 'ul' => nil,
308
+ 'li' => nil,
309
+ 'p' => nil,
310
+ 'h1' => nil,
311
+ 'h2' => nil,
312
+ 'h3' => nil,
313
+ 'h4' => nil,
314
+ 'h5' => nil,
315
+ 'h6' => nil,
316
+ 'blockquote' => ['cite'],
317
+ 'notextile' => nil
318
+ }
319
+
320
+ # Clean unauthorized tags.
321
+ def clean_html( text, allowed_tags = BASIC_TAGS )
322
+ text.gsub!( /<!\[CDATA\[/, '' )
323
+ text.gsub!( /<(\/*)([A-Za-z]\w*)([^>]*?)(\s?\/?)>/ ) do |m|
324
+ raw = $~
325
+ tag = raw[2].downcase
326
+ if allowed_tags.has_key? tag
327
+ pcs = [tag]
328
+ allowed_tags[tag].each do |prop|
329
+ ['"', "'", ''].each do |q|
330
+ q2 = ( q != '' ? q : '\s' )
331
+ if raw[3] =~ /#{prop}\s*=\s*#{q}([^#{q2}]+)#{q}/i
332
+ attrv = $1
333
+ next if (prop == 'src' or prop == 'href') and not attrv =~ %r{^(http|https|ftp):}
334
+ pcs << "#{prop}=\"#{attrv.gsub('"', '\\"')}\""
335
+ break
336
+ end
337
+ end
338
+ end if allowed_tags[tag]
339
+ "<#{raw[1]}#{pcs.join " "}#{raw[4]}>"
340
+ else # Unauthorized tag
341
+ if block_given?
342
+ yield m
343
+ else
344
+ ''
345
+ end
346
+ end
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,249 @@
1
+ require 'yaml'
2
+
3
+ module RedCloth::Formatters::LATEX
4
+ include RedCloth::Formatters::Base
5
+
6
+ ENTITIES = YAML::load(File.read(File.dirname(__FILE__)+'/latex_entities.yml'))
7
+
8
+ module Settings
9
+ # Maps CSS style names to latex formatting options
10
+ def latex_image_styles
11
+ @latex_image_class_styles ||= {}
12
+ end
13
+ end
14
+
15
+ RedCloth::TextileDoc.send(:include, Settings)
16
+
17
+ def escape(text)
18
+ latex_esc(text)
19
+ end
20
+
21
+ def escape_pre(text)
22
+ text
23
+ end
24
+
25
+ # headers
26
+ { :h1 => 'section*',
27
+ :h2 => 'subsection*',
28
+ :h3 => 'subsubsection*',
29
+ :h4 => 'textbf',
30
+ :h5 => 'textbf',
31
+ :h6 => 'textbf',
32
+ }.each do |m,tag|
33
+ define_method(m) do |opts|
34
+ "\\#{tag}{#{opts[:text]}}\n\n"
35
+ end
36
+ end
37
+
38
+ # commands
39
+ { :strong => 'textbf',
40
+ :em => 'emph',
41
+ :i => 'emph',
42
+ :b => 'textbf',
43
+ :ins => 'underline',
44
+ :del => 'sout',
45
+ :acronym => 'MakeUppercase',
46
+ :caps => 'MakeUppercase',
47
+ }.each do |m,tag|
48
+ define_method(m) do |opts|
49
+ "\\#{tag}{#{opts[:text]}}"
50
+ end
51
+ end
52
+
53
+ { :sup => '\ensuremath{^\textrm{#1}}',
54
+ :sub => '\ensuremath{_\textrm{#1}}',
55
+ }.each do |m, expr|
56
+ define_method(m) do |opts|
57
+ expr.sub('#1', opts[:text])
58
+ end
59
+ end
60
+
61
+ # environments
62
+ { :pre => 'verbatim',
63
+ :code => 'verbatim',
64
+ :cite => 'quote',
65
+ }.each do |m, env|
66
+ define_method(m) do |opts|
67
+ begin_chunk(env) + opts[:text] + end_chunk(env)
68
+ end
69
+ end
70
+
71
+ # ignore (or find a good solution later)
72
+ [ :span,
73
+ :div,
74
+ ].each do |m|
75
+ define_method(m) do |opts|
76
+ opts[:text].to_s
77
+ end
78
+ end
79
+
80
+ { :ol => 'enumerate',
81
+ :ul => 'itemize',
82
+ }.each do |m, env|
83
+ define_method("#{m}_open") do |opts|
84
+ opts[:block] = true
85
+ "\\begin{#{env}}\n"
86
+ end
87
+ define_method("#{m}_close") do |opts|
88
+ "#{li_close}\\end{#{env}}\n\n"
89
+ end
90
+ end
91
+
92
+ def li_open(opts)
93
+ "#{li_close unless opts.delete(:first)}\t\\item #{opts[:text]}"
94
+ end
95
+
96
+ def li_close(opts=nil)
97
+ "\n"
98
+ end
99
+
100
+ def p(opts)
101
+ opts[:text] + "\n\n"
102
+ end
103
+
104
+ def td(opts)
105
+ "\t\t\t#{opts[:text]} &\n"
106
+ end
107
+
108
+ def tr_open(opts)
109
+ "\t\t"
110
+ end
111
+
112
+ def tr_close(opts)
113
+ "\t\t\\\\\n"
114
+ end
115
+
116
+ # FIXME: we need to know the column count before opening tabular context.
117
+ def table_open(opts)
118
+ "\\begin{align*}\n"
119
+ end
120
+
121
+ def table_close(opts)
122
+ "\t\\end{align*}\n"
123
+ end
124
+
125
+ def bc_open(opts)
126
+ opts[:block] = true
127
+ begin_chunk("verbatim") + "\n"
128
+ end
129
+
130
+ def bc_close(opts)
131
+ end_chunk("verbatim") + "\n"
132
+ end
133
+
134
+ def bq_open(opts)
135
+ opts[:block] = true
136
+ "\\begin{quotation}\n"
137
+ end
138
+
139
+ def bq_close(opts)
140
+ "\\end{quotation}\n\n"
141
+ end
142
+
143
+ def link(opts)
144
+ "\\href{#{opts[:href]}}{#{opts[:name]}}"
145
+ end
146
+
147
+ # FIXME: use includegraphics with security verification
148
+ #
149
+ # Remember to use '\RequirePackage{graphicx}' in your LaTeX header
150
+ #
151
+ # FIXME: Look at dealing with width / height gracefully as this should be
152
+ # specified in a unit like cm rather than px.
153
+ def image(opts)
154
+ # Don't know how to use remote links, plus can we trust them?
155
+ return "" if opts[:src] =~ /^\w+\:\/\//
156
+ # Resolve CSS styles if any have been set
157
+ styling = opts[:class].to_s.split(/\s+/).collect { |style| latex_image_styles[style] }.compact.join ','
158
+ # Build latex code
159
+ [ "\\begin{figure}[htp]",
160
+ " \\includegraphics[#{styling}]{#{opts[:src]}}",
161
+ (" \\caption{#{escape opts[:title]}}" if opts[:title]),
162
+ (" \\label{#{escape opts[:alt]}}" if opts[:alt]),
163
+ "\\end{figure}",
164
+ ].compact.join "\n"
165
+ end
166
+
167
+ def footno(opts)
168
+ # TODO: insert a placeholder until we know the footnote content.
169
+ # For this to work, we need some kind of post-processing...
170
+ "\\footnotemark[#{opts[:text]}]"
171
+ end
172
+
173
+ def fn(opts)
174
+ "\\footnotetext[#{opts[:id]}]{#{opts[:text]}}"
175
+ end
176
+
177
+ def snip(opts)
178
+ "\\begin{verbatim}#{opts[:text]}\\end{verbatim}"
179
+ end
180
+
181
+ def quote1(opts)
182
+ "`#{opts[:text]}'"
183
+ end
184
+
185
+ def quote2(opts)
186
+ "``#{opts[:text]}\""
187
+ end
188
+
189
+ def ellipsis(opts)
190
+ "#{opts[:text]}\\ldots{}"
191
+ end
192
+
193
+ def emdash(opts)
194
+ "---"
195
+ end
196
+
197
+ def endash(opts)
198
+ "--"
199
+ end
200
+
201
+ def arrow(opts)
202
+ "\\rightarrow{}"
203
+ end
204
+
205
+ def trademark(opts)
206
+ "\\texttrademark{}"
207
+ end
208
+
209
+ def registered(opts)
210
+ "\\textregistered{}"
211
+ end
212
+
213
+ def copyright(opts)
214
+ "\\copyright{}"
215
+ end
216
+
217
+ # TODO: what do we do with (unknown) unicode entities ?
218
+ #
219
+ def entity(opts)
220
+ text = opts[:text][0..0] == '#' ? opts[:text][1..-1] : opts[:text]
221
+ ENTITIES[text]
222
+ end
223
+
224
+ def dim(opts)
225
+ space = opts[:space] ? " " : ''
226
+ "#{opts[:text]}#{space}\\texttimes{}#{space}"
227
+ end
228
+
229
+ private
230
+
231
+ # Use this for block level commands that use \begin
232
+ def begin_chunk(type)
233
+ chunk_counter[type] += 1
234
+ return "\\begin{#{type}}" if 1 == chunk_counter[type]
235
+ ''
236
+ end
237
+
238
+ # Use this for block level commands that use \end
239
+ def end_chunk(type)
240
+ chunk_counter[type] -= 1
241
+ raise RuntimeError, "Bad latex #{type} nesting detected" if chunk_counter[type] < 0 # This should never need to happen
242
+ return "\\end{#{type}}" if 0 == chunk_counter[type]
243
+ ''
244
+ end
245
+
246
+ def chunk_counter
247
+ @chunk_counter ||= Hash.new 0
248
+ end
249
+ end