maiku 0.6.1.maiku

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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