maiku 0.6.1.maiku

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/lib/maruku.rb +141 -0
  2. data/lib/maruku/attributes.rb +175 -0
  3. data/lib/maruku/defaults.rb +71 -0
  4. data/lib/maruku/errors_management.rb +92 -0
  5. data/lib/maruku/ext/div.rb +133 -0
  6. data/lib/maruku/ext/math.rb +41 -0
  7. data/lib/maruku/ext/math/elements.rb +27 -0
  8. data/lib/maruku/ext/math/latex_fix.rb +12 -0
  9. data/lib/maruku/ext/math/mathml_engines/blahtex.rb +107 -0
  10. data/lib/maruku/ext/math/mathml_engines/itex2mml.rb +29 -0
  11. data/lib/maruku/ext/math/mathml_engines/none.rb +20 -0
  12. data/lib/maruku/ext/math/mathml_engines/ritex.rb +24 -0
  13. data/lib/maruku/ext/math/parsing.rb +119 -0
  14. data/lib/maruku/ext/math/to_html.rb +187 -0
  15. data/lib/maruku/ext/math/to_latex.rb +26 -0
  16. data/lib/maruku/helpers.rb +260 -0
  17. data/lib/maruku/input/charsource.rb +326 -0
  18. data/lib/maruku/input/extensions.rb +69 -0
  19. data/lib/maruku/input/html_helper.rb +189 -0
  20. data/lib/maruku/input/linesource.rb +111 -0
  21. data/lib/maruku/input/parse_block.rb +616 -0
  22. data/lib/maruku/input/parse_doc.rb +232 -0
  23. data/lib/maruku/input/parse_span_better.rb +746 -0
  24. data/lib/maruku/input/rubypants.rb +225 -0
  25. data/lib/maruku/input/type_detection.rb +147 -0
  26. data/lib/maruku/input_textile2/t2_parser.rb +163 -0
  27. data/lib/maruku/maruku.rb +33 -0
  28. data/lib/maruku/output/s5/fancy.rb +756 -0
  29. data/lib/maruku/output/s5/to_s5.rb +138 -0
  30. data/lib/maruku/output/to_html.rb +991 -0
  31. data/lib/maruku/output/to_latex.rb +590 -0
  32. data/lib/maruku/output/to_latex_entities.rb +367 -0
  33. data/lib/maruku/output/to_latex_strings.rb +64 -0
  34. data/lib/maruku/output/to_markdown.rb +164 -0
  35. data/lib/maruku/output/to_s.rb +56 -0
  36. data/lib/maruku/string_utils.rb +201 -0
  37. data/lib/maruku/structures.rb +167 -0
  38. data/lib/maruku/structures_inspect.rb +87 -0
  39. data/lib/maruku/structures_iterators.rb +61 -0
  40. data/lib/maruku/textile2.rb +1 -0
  41. data/lib/maruku/toc.rb +199 -0
  42. data/lib/maruku/usage/example1.rb +33 -0
  43. data/lib/maruku/version.rb +39 -0
  44. metadata +167 -0
@@ -0,0 +1,111 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ module MaRuKu; module In; module Markdown; module BlockLevelParser
23
+
24
+ # This represents a source of lines that can be consumed.
25
+ #
26
+ # It is the twin of CharSource.
27
+ #
28
+
29
+ class LineSource
30
+ include MaRuKu::Strings
31
+ attr_reader :parent
32
+
33
+ def initialize(lines, parent=nil, parent_offset=nil)
34
+ raise "NIL lines? " if not lines
35
+ @lines = lines
36
+ @lines_index = 0
37
+ @parent = parent
38
+ @parent_offset = parent_offset
39
+ end
40
+
41
+ def cur_line() @lines[@lines_index] end
42
+ def next_line() @lines[@lines_index+1] end
43
+
44
+ def shift_line()
45
+ raise "Over the rainbow" if @lines_index >= @lines.size
46
+ l = @lines[@lines_index]
47
+ @lines_index += 1
48
+ return l
49
+ end
50
+
51
+ def ignore_line
52
+ raise "Over the rainbow" if @lines_index >= @lines.size
53
+ @lines_index += 1
54
+ end
55
+
56
+ def describe
57
+ s = "At line #{original_line_number(@lines_index)}\n"
58
+
59
+ context = 3 # lines
60
+ from = [@lines_index-context, 0].max
61
+ to = [@lines_index+context, @lines.size-1].min
62
+
63
+ for i in from..to
64
+ prefix = (i == @lines_index) ? '--> ' : ' ';
65
+ l = @lines[i]
66
+ s += "%10s %4s|%s" %
67
+ [@lines[i].md_type.to_s, prefix, l]
68
+
69
+ s += "|\n"
70
+ end
71
+
72
+ # if @parent
73
+ # s << "Parent context is: \n"
74
+ # s << add_tabs(@parent.describe,1,'|')
75
+ # end
76
+ s
77
+ end
78
+
79
+ def original_line_number(index)
80
+ if @parent
81
+ return index + @parent.original_line_number(@parent_offset)
82
+ else
83
+ 1 + index
84
+ end
85
+ end
86
+
87
+ def cur_index
88
+ @lines_index
89
+ end
90
+
91
+ # Returns the type of next line as a string
92
+ # breaks at first :definition
93
+ def tell_me_the_future
94
+ s = ""; num_e = 0;
95
+ for i in @lines_index..@lines.size-1
96
+ c = case @lines[i].md_type
97
+ when :text; "t"
98
+ when :empty; num_e+=1; "e"
99
+ when :definition; "d"
100
+ else "o"
101
+ end
102
+ s += c
103
+ break if c == "d" or num_e>1
104
+ end
105
+ s
106
+ end
107
+
108
+ end # linesource
109
+
110
+ end end end end # block
111
+
@@ -0,0 +1,616 @@
1
+ #--
2
+ # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
3
+ #
4
+ # This file is part of Maruku.
5
+ #
6
+ # Maruku is free software; you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation; either version 2 of the License, or
9
+ # (at your option) any later version.
10
+ #
11
+ # Maruku is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+ #
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Maruku; if not, write to the Free Software
18
+ # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
20
+
21
+
22
+ module MaRuKu; module In; module Markdown; module BlockLevelParser
23
+
24
+ include Helpers
25
+ include MaRuKu::Strings
26
+ include MaRuKu::In::Markdown::SpanLevelParser
27
+
28
+ class BlockContext < Array
29
+ def describe
30
+ n = 5
31
+ desc = size > n ? self[-n,n] : self
32
+ "Last #{n} elements: "+
33
+ desc.map{|x| "\n -" + x.inspect}.join
34
+ end
35
+ end
36
+
37
+ # Splits the string and calls parse_lines_as_markdown
38
+ def parse_text_as_markdown(text)
39
+ lines = split_lines(text)
40
+ src = LineSource.new(lines)
41
+ return parse_blocks(src)
42
+ end
43
+
44
+ # Input is a LineSource
45
+ def parse_blocks(src)
46
+ output = BlockContext.new
47
+
48
+ # run state machine
49
+ while src.cur_line
50
+
51
+ next if check_block_extensions(src, output, src.cur_line)
52
+
53
+ # Prints detected type (useful for debugging)
54
+ # puts "#{src.cur_line.md_type}|#{src.cur_line}"
55
+ case src.cur_line.md_type
56
+ when :empty;
57
+ output.push :empty
58
+ src.ignore_line
59
+ when :ial
60
+ m = InlineAttributeList.match src.shift_line
61
+ content = m[1] || ""
62
+ # puts "Content: #{content.inspect}"
63
+ src2 = CharSource.new(content, src)
64
+ interpret_extension(src2, output, [nil])
65
+ when :ald
66
+ output.push read_ald(src)
67
+ when :text
68
+ # paragraph, or table, or definition list
69
+ read_text_material(src, output)
70
+ when :header2, :hrule
71
+ # hrule
72
+ src.shift_line
73
+ output.push md_hrule()
74
+ when :header3
75
+ output.push read_header3(src)
76
+ when :ulist, :olist
77
+ list_type = src.cur_line.md_type == :ulist ? :ul : :ol
78
+ li = read_list_item(src)
79
+ # append to current list if we have one
80
+ if output.last.kind_of?(MDElement) &&
81
+ output.last.node_type == list_type then
82
+ output.last.children << li
83
+ else
84
+ output.push md_el(list_type, [li])
85
+ end
86
+ when :quote; output.push read_quote(src)
87
+ when :code; e = read_code(src); output << e if e
88
+ when :raw_html; e = read_raw_html(src); output << e if e
89
+
90
+ when :footnote_text; output.push read_footnote_text(src)
91
+ when :ref_definition;
92
+ if src.parent && (src.cur_index == 0)
93
+ read_text_material(src, output)
94
+ else
95
+ read_ref_definition(src, output)
96
+ end
97
+ when :abbreviation; output.push read_abbreviation(src)
98
+ when :xml_instr; read_xml_instruction(src, output)
99
+ when :metadata;
100
+ maruku_error "Please use the new meta-data syntax: \n"+
101
+ " http://maruku.rubyforge.org/proposal.html\n", src
102
+ src.ignore_line
103
+ else # warn if we forgot something
104
+ md_type = src.cur_line.md_type
105
+ line = src.cur_line
106
+ maruku_error "Ignoring line '#{line}' type = #{md_type}", src
107
+ src.shift_line
108
+ end
109
+ end
110
+
111
+ merge_ial(output, src, output)
112
+ output.delete_if {|x| x.kind_of?(MDElement) &&
113
+ x.node_type == :ial}
114
+
115
+ # get rid of empty line markers
116
+ output.delete_if {|x| x == :empty}
117
+ # See for each list if we can omit the paragraphs and use li_span
118
+ # TODO: do this after
119
+ output.each do |c|
120
+ # Remove paragraphs that we can get rid of
121
+ if [:ul,:ol].include? c.node_type
122
+ if c.children.all? {|li| !li.want_my_paragraph} then
123
+ c.children.each do |d|
124
+ d.node_type = :li_span
125
+ d.children = d.children[0].children
126
+ end
127
+ end
128
+ end
129
+ if c.node_type == :definition_list
130
+ if c.children.all?{|defi| !defi.want_my_paragraph} then
131
+ c.children.each do |definition|
132
+ definition.definitions.each do |dd|
133
+ dd.children = dd.children[0].children
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+
140
+ output
141
+ end
142
+
143
+ def read_text_material(src, output)
144
+ if src.cur_line =~ MightBeTableHeader and
145
+ (src.next_line && src.next_line =~ TableSeparator)
146
+ output.push read_table(src)
147
+ elsif [:header1,:header2].include? src.next_line.md_type
148
+ output.push read_header12(src)
149
+ elsif eventually_comes_a_def_list(src)
150
+ definition = read_definition(src)
151
+ if output.last.kind_of?(MDElement) &&
152
+ output.last.node_type == :definition_list then
153
+ output.last.children << definition
154
+ else
155
+ output.push md_el(:definition_list, [definition])
156
+ end
157
+ else # Start of a paragraph
158
+ output.push read_paragraph(src)
159
+ end
160
+ end
161
+
162
+
163
+ def read_ald(src)
164
+ if (l=src.shift_line) =~ AttributeDefinitionList
165
+ id = $1; al=$2;
166
+ al = read_attribute_list(CharSource.new(al,src), context=nil, break_on=[nil])
167
+ self.ald[id] = al;
168
+ return md_ald(id, al)
169
+ else
170
+ maruku_error "Bug Bug:\n#{l.inspect}"
171
+ return nil
172
+ end
173
+ end
174
+
175
+ # reads a header (with ----- or ========)
176
+ def read_header12(src)
177
+ line = src.shift_line.strip
178
+ al = nil
179
+ # Check if there is an IAL
180
+ if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
181
+ line = $1.strip
182
+ ial = $2
183
+ al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
184
+ end
185
+ text = parse_lines_as_span [ line ]
186
+ level = src.cur_line.md_type == :header2 ? 2 : 1;
187
+ src.shift_line
188
+ return md_header(level, text, al)
189
+ end
190
+
191
+ # reads a header like '#### header ####'
192
+ def read_header3(src)
193
+ line = src.shift_line.strip
194
+ al = nil
195
+ # Check if there is an IAL
196
+ if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
197
+ line = $1.strip
198
+ ial = $2
199
+ al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
200
+ end
201
+ level = num_leading_hashes(line)
202
+ text = parse_lines_as_span [strip_hashes(line)]
203
+ return md_header(level, text, al)
204
+ end
205
+
206
+ def read_xml_instruction(src, output)
207
+ m = /^\s*<\?((\w+)\s*)?(.*)$/.match src.shift_line
208
+ raise "BugBug" if not m
209
+ target = m[2] || ''
210
+ code = m[3]
211
+ until code =~ /\?>/
212
+ code += "\n"+src.shift_line
213
+ end
214
+ if not code =~ (/\?>\s*$/)
215
+ garbage = (/\?>(.*)$/.match(code))[1]
216
+ maruku_error "Trailing garbage on last line: #{garbage.inspect}:\n"+
217
+ add_tabs(code, 1, '|'), src
218
+ end
219
+ code.gsub!(/\?>\s*$/, '')
220
+
221
+ if target == 'mrk' && MaRuKu::Globals[:unsafe_features]
222
+ result = safe_execute_code(self, code)
223
+ if result
224
+ if result.kind_of? String
225
+ raise "Not expected"
226
+ else
227
+ output.push(*result)
228
+ end
229
+ end
230
+ else
231
+ output.push md_xml_instr(target, code)
232
+ end
233
+ end
234
+
235
+ def read_raw_html(src)
236
+ h = HTMLHelper.new
237
+ begin
238
+ h.eat_this(l=src.shift_line)
239
+ # puts "\nBLOCK:\nhtml -> #{l.inspect}"
240
+ while src.cur_line and not h.is_finished?
241
+ l=src.shift_line
242
+ # puts "html -> #{l.inspect}"
243
+ h.eat_this "\n"+l
244
+ end
245
+ rescue Exception => e
246
+ ex = e.inspect + e.backtrace.join("\n")
247
+ maruku_error "Bad block-level HTML:\n#{add_tabs(ex,1,'|')}\n", src
248
+ end
249
+ if not (h.rest =~ /^\s*$/)
250
+ maruku_error "Could you please format this better?\n"+
251
+ "I see that #{h.rest.inspect} is left after the raw HTML.", src
252
+ end
253
+ raw_html = h.stuff_you_read
254
+
255
+ return md_html(raw_html)
256
+ end
257
+
258
+ def read_paragraph(src)
259
+ lines = [src.shift_line]
260
+ while src.cur_line
261
+ # :olist does not break
262
+ case t = src.cur_line.md_type
263
+ when :quote,:header3,:empty,:ref_definition,:ial #,:xml_instr,:raw_html
264
+ break
265
+ when :olist,:ulist
266
+ break if src.next_line.md_type == t
267
+ end
268
+ break if src.cur_line.strip.size == 0
269
+ break if [:header1,:header2].include? src.next_line.md_type
270
+ break if any_matching_block_extension?(src.cur_line)
271
+
272
+ lines << src.shift_line
273
+ end
274
+ # dbg_describe_ary(lines, 'PAR')
275
+ children = parse_lines_as_span(lines, src)
276
+
277
+ return md_par(children)
278
+ end
279
+
280
+ # Reads one list item, either ordered or unordered.
281
+ def read_list_item(src)
282
+ parent_offset = src.cur_index
283
+
284
+ item_type = src.cur_line.md_type
285
+ first = src.shift_line
286
+
287
+ indentation, ial = spaces_before_first_char(first)
288
+ al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil]) if ial
289
+ break_list = [:ulist, :olist, :ial]
290
+ # Ugly things going on inside `read_indented_content`
291
+ lines, want_my_paragraph =
292
+ read_indented_content(src,indentation, break_list, item_type)
293
+
294
+ # add first line
295
+ # Strip first '*', '-', '+' from first line
296
+ stripped = first[indentation, first.size-1]
297
+ lines.unshift stripped
298
+
299
+ # dbg_describe_ary(lines, 'LIST ITEM ')
300
+
301
+ src2 = LineSource.new(lines, src, parent_offset)
302
+ children = parse_blocks(src2)
303
+ with_par = want_my_paragraph || (children.size>1)
304
+
305
+ return md_li(children, with_par, al)
306
+ end
307
+
308
+ def read_abbreviation(src)
309
+ if not (l=src.shift_line) =~ Abbreviation
310
+ maruku_error "Bug: it's Andrea's fault. Tell him.\n#{l.inspect}"
311
+ end
312
+
313
+ abbr = $1
314
+ desc = $2
315
+
316
+ if (not abbr) or (abbr.size==0)
317
+ maruku_error "Bad abbrev. abbr=#{abbr.inspect} desc=#{desc.inspect}"
318
+ end
319
+
320
+ self.abbreviations[abbr] = desc
321
+
322
+ return md_abbr_def(abbr, desc)
323
+ end
324
+
325
+ def read_footnote_text(src)
326
+ parent_offset = src.cur_index
327
+
328
+ first = src.shift_line
329
+
330
+ if not first =~ FootnoteText
331
+ maruku_error "Bug (it's Andrea's fault)"
332
+ end
333
+
334
+ id = $1
335
+ text = $2
336
+
337
+ # Ugly things going on inside `read_indented_content`
338
+ indentation = 4 #first.size-text.size
339
+
340
+ # puts "id =_#{id}_; text=_#{text}_ indent=#{indentation}"
341
+
342
+ break_list = [:footnote_text, :ref_definition, :definition, :abbreviation]
343
+ item_type = :footnote_text
344
+ lines, want_my_paragraph =
345
+ read_indented_content(src,indentation, break_list, item_type)
346
+
347
+ # add first line
348
+ if text && text.strip != "" then lines.unshift text end
349
+
350
+ # dbg_describe_ary(lines, 'FOOTNOTE')
351
+ src2 = LineSource.new(lines, src, parent_offset)
352
+ children = parse_blocks(src2)
353
+
354
+ e = md_footnote(id, children)
355
+ self.footnotes[id] = e
356
+ return e
357
+ end
358
+
359
+
360
+ # This is the only ugly function in the code base.
361
+ # It is used to read list items, descriptions, footnote text
362
+ def read_indented_content(src, indentation, break_list, item_type)
363
+ lines =[]
364
+ # collect all indented lines
365
+ saw_empty = false; saw_anything_after = false
366
+ while src.cur_line
367
+ # puts "Reading indent = #{indentation} #{src.cur_line.inspect}"
368
+ #puts "#{src.cur_line.md_type} #{src.cur_line.inspect}"
369
+ if src.cur_line.md_type == :empty
370
+ saw_empty = true
371
+ lines << src.shift_line
372
+ next
373
+ end
374
+
375
+ # after a white line
376
+ if saw_empty
377
+ # we expect things to be properly aligned
378
+ if (ns=number_of_leading_spaces(src.cur_line)) < indentation
379
+ #puts "breaking for spaces, only #{ns}: #{src.cur_line}"
380
+ break
381
+ end
382
+ saw_anything_after = true
383
+ else
384
+ # if src.cur_line[0] != ?\
385
+ break if break_list.include? src.cur_line.md_type
386
+ # end
387
+ # break if src.cur_line.md_type != :text
388
+ end
389
+
390
+
391
+ stripped = strip_indent(src.shift_line, indentation)
392
+ lines << stripped
393
+
394
+ #puts "Accepted as #{stripped.inspect}"
395
+
396
+ # You are only required to indent the first line of
397
+ # a child paragraph.
398
+ if stripped.md_type == :text
399
+ while src.cur_line && (src.cur_line.md_type == :text)
400
+ lines << strip_indent(src.shift_line, indentation)
401
+ end
402
+ end
403
+ end
404
+
405
+ want_my_paragraph = saw_anything_after ||
406
+ (saw_empty && (src.cur_line && (src.cur_line.md_type == item_type)))
407
+
408
+ # dbg_describe_ary(lines, 'LI')
409
+ # create a new context
410
+
411
+ while lines.last && (lines.last.md_type == :empty)
412
+ lines.pop
413
+ end
414
+
415
+ return lines, want_my_paragraph
416
+ end
417
+
418
+
419
+ def read_quote(src)
420
+ parent_offset = src.cur_index
421
+
422
+ lines = []
423
+ # collect all indented lines
424
+ while src.cur_line && src.cur_line.md_type == :quote
425
+ lines << unquote(src.shift_line)
426
+ end
427
+ # dbg_describe_ary(lines, 'QUOTE')
428
+
429
+ src2 = LineSource.new(lines, src, parent_offset)
430
+ children = parse_blocks(src2)
431
+ return md_quote(children)
432
+ end
433
+
434
+ def read_code(src)
435
+ # collect all indented lines
436
+ lines = []
437
+ while src.cur_line && ([:code, :empty].include? src.cur_line.md_type)
438
+ lines << strip_indent(src.shift_line, 4)
439
+ end
440
+
441
+ #while lines.last && (lines.last.md_type == :empty )
442
+ while lines.last && lines.last.strip.size == 0
443
+ lines.pop
444
+ end
445
+
446
+ while lines.first && lines.first.strip.size == 0
447
+ lines.shift
448
+ end
449
+
450
+ return nil if lines.empty?
451
+
452
+ source = lines.join("\n")
453
+
454
+ # dbg_describe_ary(lines, 'CODE')
455
+
456
+ return md_codeblock(source)
457
+ end
458
+
459
+ # Reads a series of metadata lines with empty lines in between
460
+ def read_metadata(src)
461
+ hash = {}
462
+ while src.cur_line
463
+ case src.cur_line.md_type
464
+ when :empty; src.shift_line
465
+ when :metadata; hash.merge! parse_metadata(src.shift_line)
466
+ else break
467
+ end
468
+ end
469
+ hash
470
+ end
471
+
472
+
473
+ def read_ref_definition(src, out)
474
+ line = src.shift_line
475
+
476
+
477
+ # if link is incomplete, shift next line
478
+ if src.cur_line && !([:footnote_text, :ref_definition, :definition, :abbreviation].include? src.cur_line.md_type) &&
479
+ ([1,2,3].include? number_of_leading_spaces(src.cur_line) )
480
+ line += " "+ src.shift_line
481
+ end
482
+
483
+ # puts "total= #{line}"
484
+
485
+ match = LinkRegex.match(line)
486
+ if not match
487
+ maruku_error "Link does not respect format: '#{line}'"
488
+ return
489
+ end
490
+
491
+ id = match[1]; url = match[2]; title = match[3];
492
+ id = sanitize_ref_id(id)
493
+
494
+ hash = self.refs[id] = {:url=>url,:title=>title}
495
+
496
+ stuff=match[4]
497
+
498
+ if stuff
499
+ stuff.split.each do |couple|
500
+ # puts "found #{couple}"
501
+ k, v = couple.split('=')
502
+ v ||= ""
503
+ if v[0,1]=='"' then v = v[1, v.size-2] end
504
+ # puts "key:_#{k}_ value=_#{v}_"
505
+ hash[k.to_sym] = v
506
+ end
507
+ end
508
+ # puts hash.inspect
509
+
510
+ out.push md_ref_def(id, url, meta={:title=>title})
511
+ end
512
+
513
+ def split_cells(s)
514
+ # s.strip.split('|').select{|x|x.strip.size>0}.map{|x|x.strip}
515
+ # changed to allow empty cells
516
+ s.strip.split('|').select{|x|x.size>0}.map{|x|x.strip}
517
+ end
518
+
519
+ def read_table(src)
520
+ head = split_cells(src.shift_line).map{|s| md_el(:head_cell, parse_lines_as_span([s])) }
521
+
522
+ separator=split_cells(src.shift_line)
523
+
524
+ align = separator.map { |s| s =~ Sep
525
+ if $1 and $2 then :center elsif $2 then :right else :left end }
526
+
527
+ num_columns = align.size
528
+
529
+ if head.size != num_columns
530
+ maruku_error "Table head does not have #{num_columns} columns: \n#{head.inspect}"
531
+ tell_user "I will ignore this table."
532
+ # XXX try to recover
533
+ return md_br()
534
+ end
535
+
536
+ rows = []
537
+
538
+ while src.cur_line && src.cur_line =~ /\|/
539
+ row = split_cells(src.shift_line).map{|s|
540
+ md_el(:cell, parse_lines_as_span([s]))}
541
+ if head.size != num_columns
542
+ maruku_error "Row does not have #{num_columns} columns: \n#{row.inspect}"
543
+ tell_user "I will ignore this table."
544
+ # XXX try to recover
545
+ return md_br()
546
+ end
547
+ rows << row
548
+ end
549
+
550
+ children = (head+rows).flatten
551
+ return md_el(:table, children, {:align => align})
552
+ end
553
+
554
+ # If current line is text, a definition list is coming
555
+ # if 1) text,empty,[text,empty]*,definition
556
+
557
+ def eventually_comes_a_def_list(src)
558
+ future = src.tell_me_the_future
559
+ ok = future =~ %r{^t+e?d}x
560
+ # puts "future: #{future} - #{ok}"
561
+ ok
562
+ end
563
+
564
+
565
+ def read_definition(src)
566
+ # Read one or more terms
567
+ terms = []
568
+ while src.cur_line && src.cur_line.md_type == :text
569
+ terms << md_el(:definition_term, parse_lines_as_span([src.shift_line]))
570
+ end
571
+ # dbg_describe_ary(terms, 'DT')
572
+
573
+ want_my_paragraph = false
574
+
575
+ raise "Chunky Bacon!" if not src.cur_line
576
+
577
+ # one optional empty
578
+ if src.cur_line.md_type == :empty
579
+ want_my_paragraph = true
580
+ src.shift_line
581
+ end
582
+
583
+ raise "Chunky Bacon!" if src.cur_line.md_type != :definition
584
+
585
+ # Read one or more definitions
586
+ definitions = []
587
+ while src.cur_line && src.cur_line.md_type == :definition
588
+ parent_offset = src.cur_index
589
+
590
+ first = src.shift_line
591
+ first =~ Definition
592
+ first = $1
593
+
594
+ # I know, it's ugly!!!
595
+
596
+ lines, w_m_p =
597
+ read_indented_content(src,4, [:definition], :definition)
598
+ want_my_paragraph ||= w_m_p
599
+
600
+ lines.unshift first
601
+
602
+ # dbg_describe_ary(lines, 'DD')
603
+ src2 = LineSource.new(lines, src, parent_offset)
604
+ children = parse_blocks(src2)
605
+ definitions << md_el(:definition_data, children)
606
+ end
607
+
608
+ return md_el(:definition, terms+definitions, {
609
+ :terms => terms,
610
+ :definitions => definitions,
611
+ :want_my_paragraph => want_my_paragraph})
612
+ end
613
+ end # BlockLevelParser
614
+ end # MaRuKu
615
+ end
616
+ end