maruku 0.2

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 (60) hide show
  1. data/bin/maruku +25 -0
  2. data/bin/marutex +29 -0
  3. data/docs/Makefile +25 -0
  4. data/docs/char_codes.xml +884 -0
  5. data/docs/color-package-demo.aux +1 -0
  6. data/docs/color-package-demo.log +127 -0
  7. data/docs/color-package-demo.tex +149 -0
  8. data/docs/index.html +74 -0
  9. data/docs/markdown_syntax.aux +13 -0
  10. data/docs/markdown_syntax.html +266 -0
  11. data/docs/markdown_syntax.log +287 -0
  12. data/docs/markdown_syntax.md +920 -0
  13. data/docs/markdown_syntax.out +0 -0
  14. data/docs/markdown_syntax.pdf +0 -0
  15. data/docs/markdown_syntax.tex +1203 -0
  16. data/docs/maruku.aux +13 -0
  17. data/docs/maruku.html +74 -0
  18. data/docs/maruku.log +294 -0
  19. data/docs/maruku.md +394 -0
  20. data/docs/maruku.out +0 -0
  21. data/docs/maruku.pdf +0 -0
  22. data/docs/maruku.tex +548 -0
  23. data/docs/style.css +65 -0
  24. data/docs/todo.md +12 -0
  25. data/lib/maruku.rb +20 -0
  26. data/lib/maruku/parse_block.rb +577 -0
  27. data/lib/maruku/parse_span.rb +336 -0
  28. data/lib/maruku/string_utils.rb +270 -0
  29. data/lib/maruku/structures.rb +31 -0
  30. data/lib/maruku/to_html.rb +430 -0
  31. data/lib/maruku/to_latex.rb +345 -0
  32. data/lib/maruku/to_latex_strings.rb +330 -0
  33. data/tests/abbreviations.md +11 -0
  34. data/tests/blank.md +4 -0
  35. data/tests/code.md +5 -0
  36. data/tests/code2.md +8 -0
  37. data/tests/code3.md +16 -0
  38. data/tests/email.md +4 -0
  39. data/tests/entities.md +19 -0
  40. data/tests/escaping.md +14 -0
  41. data/tests/extra_dl.md +101 -0
  42. data/tests/extra_header_id.md +13 -0
  43. data/tests/extra_table1.md +40 -0
  44. data/tests/footnotes.md +17 -0
  45. data/tests/headers.md +10 -0
  46. data/tests/hrule.md +10 -0
  47. data/tests/images.md +20 -0
  48. data/tests/inline_html.md +35 -0
  49. data/tests/links.md +31 -0
  50. data/tests/list1.md +4 -0
  51. data/tests/list2.md +5 -0
  52. data/tests/list3.md +8 -0
  53. data/tests/lists.md +32 -0
  54. data/tests/lists_ol.md +39 -0
  55. data/tests/misc_sw.md +105 -0
  56. data/tests/one.md +1 -0
  57. data/tests/paragraphs.md +13 -0
  58. data/tests/sss06.md +352 -0
  59. data/tests/test.md +4 -0
  60. metadata +113 -0
@@ -0,0 +1,65 @@
1
+
2
+ BODY {
3
+ font-family: Georgia;
4
+ max-width: 40em;
5
+ padding-left: 3em;
6
+ }
7
+
8
+ H1 { margin-left: -1em;}
9
+ H2 { margin-left: -1em;}
10
+
11
+ PRE {
12
+ margin-left: 2em;
13
+ background-color: #eff;
14
+ border: solid 1px #bdd;
15
+ padding: 4px;
16
+ }
17
+
18
+ H1 STRONG { color: red;}
19
+
20
+ TABLE.example TD,
21
+ TABLE.example TR,
22
+ TABLE.example TH {
23
+ border: solid 1px #555;
24
+ padding: 4px;
25
+
26
+ }
27
+ /*pre SPAN { border: solid 1px black; }*/
28
+
29
+ .ruby .normal {}
30
+ .ruby .comment { color: #005; font-style: italic; }
31
+ .ruby .keyword { color: #A00; font-weight: bold; }
32
+ .ruby .method { color: #077; }
33
+ .ruby .class { color: #074; }
34
+ .ruby .module { color: #050; }
35
+ .ruby .punct { color: #447; font-weight: bold; }
36
+ .ruby .symbol { color: #099; }
37
+ .ruby .string { color: #944; background: #FFE; }
38
+ .ruby .char { color: #F07; }
39
+ .ruby .ident { color: #004; }
40
+ .ruby .constant { color: #07F; }
41
+ .ruby .regex { color: #B66; background: #FEF; }
42
+ .ruby .number { color: #F99; }
43
+ .ruby .attribute { color: #7BB; }
44
+ .ruby .global { color: #7FB; }
45
+ .ruby .expr { color: #227; }
46
+ .ruby .escape { color: #277; }
47
+
48
+ .xml .normal {}
49
+ .xml .namespace { color: #B66; font-weight: bold; }
50
+ .xml .tag { color: #F88; }
51
+ .xml .comment { color: #005; font-style: italic; }
52
+ .xml .punct { color: #447; font-weight: bold; }
53
+ .xml .string { color: #944; }
54
+ .xml .number { color: #F99; }
55
+ .xml .attribute { color: #BB7; }
56
+
57
+ .html .normal {}
58
+ .html .namespace { color: #B66; font-weight: bold; }
59
+ .html .tag { color: #F88; }
60
+ .html .comment { color: #005; font-style: italic; }
61
+ .html .punct { color: #447; font-weight: bold; }
62
+ .html .string { color: #944; }
63
+ .html .number { color: #F99; }
64
+ .html .attribute { color: #BB7; }
65
+
@@ -0,0 +1,12 @@
1
+
2
+
3
+
4
+
5
+
6
+
7
+ Latex export
8
+ ------------
9
+
10
+ * Convert HTML entities in the input to the equivalent in Latex.
11
+ For example, `$` to `\$`, ` ` to `\ `.
12
+
@@ -0,0 +1,20 @@
1
+
2
+ # Structures definition
3
+ require 'maruku/structures'
4
+
5
+ # Code for parsing block-level elements
6
+ require 'maruku/parse_block'
7
+
8
+ # Code for parsing span-level elements
9
+ require 'maruku/parse_span'
10
+
11
+ # Ugly things kept in a closet
12
+ require 'maruku/string_utils'
13
+
14
+
15
+ # Exporting to html
16
+ require 'maruku/to_html'
17
+
18
+ # Exporting to latex
19
+ require 'maruku/to_latex'
20
+ require 'maruku/to_latex_strings'
@@ -0,0 +1,577 @@
1
+
2
+ class Maruku
3
+ def initialize(s=nil)
4
+ @node_type = :document
5
+ @doc = self
6
+
7
+ @refs = {}
8
+ @footnotes = {}
9
+ @abbreviations = {}
10
+
11
+ parse_doc(s) if s
12
+ end
13
+
14
+ def parse_doc(s)
15
+ # setup initial stack
16
+ @stack = []
17
+
18
+ @meta = parse_email_headers(s)
19
+ lines = split_lines(@meta[:data])
20
+ @children = parse_lines_as_markdown(lines)
21
+
22
+ self.search_abbreviations
23
+ self.substitute_markdown_inside_raw_html
24
+ end
25
+
26
+ def search_abbreviations
27
+ @abbreviations.each do |abbrev, title|
28
+ # puts "#{abbrev} => #{title}"
29
+ self.map_match(Regexp.new(Regexp.escape(abbrev))) {
30
+ e = create_md_element(:abbreviation)
31
+ e.children = [abbrev.dup]
32
+ e.meta[:title] = title.dup if title
33
+ e
34
+ }
35
+ end
36
+ end
37
+
38
+ # (PHP Markdown extra) Search for elements that have
39
+ # markdown=1 or markdown=block defined
40
+ def substitute_markdown_inside_raw_html
41
+ self.each_element(:raw_html) do |e|
42
+ doc = e.meta[:parsed_html]
43
+ if doc # valid html
44
+ # parse block-level markdown elements in these HTML tags
45
+ block_tags = ['div']
46
+ # use xpath to find elements with 'markdown' attribute
47
+ doc.elements.to_a( "//*[attribute::markdown]" ).each do |e|
48
+ # should we parse block-level or span-level?
49
+ parse_blocks = (e.attributes['markdown'] == 'block') ||
50
+ block_tags.include?(e.name)
51
+ # remove 'markdown' attribute
52
+ e.delete_attribute 'markdown'
53
+ # Select all text elements of e
54
+ e.texts.each do |original_text|
55
+ # puts "parse_blocks = #{parse_blocks} found = #{original_text} "
56
+ s = original_text.to_s.strip # XXX
57
+ el = create_md_element(:dummy,
58
+ parse_blocks ? parse_text_as_markdown(s) :
59
+ parse_lines_as_span(s) )
60
+ el.children_to_html.each do |x|
61
+ e.insert_before(original_text, x)
62
+ end
63
+ e.delete(original_text)
64
+ end
65
+
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ # Splits the string and calls parse_lines_as_markdown
73
+ def parse_text_as_markdown(text)
74
+ lines = split_lines(text)
75
+ parse_lines_as_markdown(lines)
76
+ end
77
+
78
+ def parse_lines_as_markdown(lines)
79
+ @stack.push lines
80
+ output = []; current_metadata = just_read_metadata = nil
81
+ # run state machine
82
+ while cur_line
83
+ # puts "#{cur_line_node_type}|#{cur_line}"
84
+ case cur_line_node_type
85
+ when :empty;
86
+ shift_line;
87
+ when :text
88
+ if cur_line =~ MightBeTableHeader and
89
+ (next_line && next_line =~ TableSeparator)
90
+ output << read_table
91
+ elsif [:header1,:header2].include? next_line_node_type
92
+ e = create_md_element(:header)
93
+ line = shift_line.strip
94
+ if line =~ HeaderWithId
95
+ line = $1.strip
96
+ e.meta[:id] = $2
97
+ end
98
+ e.children = parse_lines_as_span [ line ]
99
+
100
+ e.meta[:level] = cur_line_node_type == :header2 ? 2 : 1
101
+ shift_line
102
+
103
+ output << e
104
+ elsif eventually_comes_a_def_list
105
+ definition = read_definition
106
+ if output.last && output.last.node_type == :definition_list
107
+ output.last.children << definition
108
+ else
109
+ output << create_md_element(:definition_list, [definition])
110
+ end
111
+
112
+ else # Start of a paragraph
113
+ output << read_paragraph
114
+ end
115
+ when :header2, :hrule
116
+ # hrule
117
+ shift_line
118
+ output << create_md_element(:hrule)
119
+ when :header3
120
+ e = create_md_element(:header)
121
+ line = shift_line.strip
122
+ if line =~ HeaderWithId
123
+ line = $1.strip
124
+ e.meta[:id] = $2
125
+ end
126
+
127
+ e.meta[:level] = num_leading_hashes(line)
128
+ e.children = parse_lines_as_span [strip_hashes(line)]
129
+ output << e
130
+ when :ulist, :olist
131
+ list_type = cur_line_node_type == :ulist ? :ul : :ol
132
+ li = read_list_item
133
+ # append to current list if we have one
134
+ if output.last && output.last.node_type == list_type
135
+ output.last.children << li
136
+ else
137
+ output << create_md_element(list_type, [li])
138
+ end
139
+ when :quote; output << read_quote
140
+ when :code; e = read_code; output << e if e
141
+ when :raw_html; output << read_raw_html
142
+
143
+ # these do not produce output
144
+ when :footnote_text; read_footnote_text
145
+ when :ref; read_ref
146
+ when :abbreviation; read_abbreviation
147
+ when :metadata; just_read_metadata = read_metadata
148
+
149
+ # warn if we forgot something
150
+ else
151
+ node_type = cur_line_node_type
152
+ $stderr.puts "Ignoring line '#{shift_line}' type = #{node_type}"
153
+ end
154
+
155
+ if current_metadata and output.last
156
+ output.last.meta.merge! current_metadata
157
+ current_metadata = nil
158
+ # puts "meta for #{output.last.node_type}\n #{output.last.meta.inspect}"
159
+ end
160
+ current_metadata = just_read_metadata
161
+ just_read_metadata = nil
162
+ end
163
+ # pop the stack
164
+ @stack.pop
165
+
166
+ # See for each list if we can omit the paragraphs and use li_span
167
+ output.each do |c|
168
+ # Remove paragraphs that we can get rid of
169
+ if [:ul,:ol].include? c.node_type
170
+ if c.children.all? {|li| !li.meta[:want_my_paragraph]} then
171
+ c.children.each do |d|
172
+ d.node_type = :li_span
173
+ d.children = d.children[0].children
174
+ end
175
+ end
176
+ end
177
+ if c.node_type == :definition_list
178
+ if c.children.all?{|defi| !defi.meta[:want_my_paragraph]} then
179
+ c.children.each do |definition|
180
+ dds = definition.meta[:definitions]
181
+ dds.each do |dd|
182
+ dd.children = dd.children[0].children
183
+ end
184
+ end
185
+ end
186
+ end
187
+ end
188
+
189
+ output
190
+ end
191
+
192
+ def create_md_element(node_type, children=[])
193
+ e = MDElement.new
194
+ e.node_type = node_type
195
+ e.children = children
196
+ e.doc = self
197
+ e
198
+ end
199
+
200
+ def top; @stack.last end
201
+ def cur_line_node_type; line_node_type top.first end
202
+ def cur_line; top.empty? ? nil : top.first end
203
+ def next_line; top.empty? ? nil : top[1] end
204
+ def next_line_node_type; (top.size >= 2) ? line_node_type(top[1]) : nil end
205
+ def shift_line; top.shift; end
206
+
207
+
208
+ def read_raw_html
209
+ lines = []
210
+
211
+ cur_line =~ %r{^<(\w+)}
212
+ tag = $1
213
+ # puts "Start tag = #{tag} "
214
+
215
+ while cur_line
216
+ break if (number_of_leading_spaces(cur_line) == 0) &&
217
+ (not [:raw_html, :empty].include? cur_line_node_type)
218
+
219
+ lines << shift_line
220
+ # check for a closing tag
221
+ if (lines.last =~ %r{^</(\w+)}||
222
+ lines.last =~ %r{</(\w+)>\s*$}) && $1 == tag
223
+ break
224
+ end
225
+ end
226
+
227
+ # dbg_describe_ary(lines, 'HTML')
228
+
229
+ raw_html = lines.join("\n")
230
+
231
+ e = create_md_element(:raw_html)
232
+
233
+ begin
234
+ e.meta[:parsed_html] = Document.new(raw_html)
235
+ rescue
236
+ $stderr.puts "Malformed HTML:\n#{raw_html}"
237
+ end
238
+
239
+ e.meta[:raw_html] = raw_html
240
+ e
241
+ end
242
+
243
+ def read_paragraph
244
+ lines = []
245
+ while cur_line && cur_line_node_type == :text
246
+ lines << shift_line
247
+ end
248
+ # dbg_describe_ary(lines, 'PAR')
249
+ children = parse_lines_as_span(lines)
250
+
251
+ e = create_md_element(:paragraph, children)
252
+ end
253
+
254
+
255
+
256
+ # Reads one list item, either ordered or unordered.
257
+ def read_list_item
258
+ item_type = cur_line_node_type
259
+ first = shift_line
260
+
261
+ # Ugly things going on inside `read_indented_content`
262
+ indentation = spaces_before_first_char(first)
263
+ break_list = [:ulist, :olist]
264
+ lines, want_my_paragraph =
265
+ read_indented_content(indentation, break_list, item_type)
266
+
267
+ # add first line
268
+ # Strip first '*' or '-' from first line
269
+ stripped = first[indentation, first.size-1]
270
+ lines.unshift stripped
271
+
272
+ e = create_md_element(:li)
273
+ e.children = parse_lines_as_markdown(lines)
274
+ e.meta[:want_my_paragraph] = want_my_paragraph|| (e.children.size>1)
275
+ e
276
+ end
277
+
278
+ def read_abbreviation
279
+ shift_line =~ Abbreviation
280
+ abbrev = $1
281
+ description = $2
282
+
283
+ @abbreviations[abbrev] = description
284
+ end
285
+
286
+ def read_footnote_text
287
+ first = shift_line
288
+
289
+ first =~ FootnoteText
290
+ id = $1
291
+ text = $2
292
+
293
+ # Ugly things going on inside `read_indented_content`
294
+ indentation = 4 #first.size-text.size
295
+
296
+ # puts "id =_#{id}_; text=_#{text}_ indent=#{indentation}"
297
+
298
+ break_list = [:footnote_text]
299
+ item_type = :footnote_text
300
+ lines, want_my_paragraph =
301
+ read_indented_content(indentation, break_list, item_type)
302
+
303
+ # add first line
304
+ if text && text.strip != "" then lines.unshift text end
305
+
306
+
307
+ # dbg_describe_ary(lines, 'FOOTNOTE')
308
+ children = parse_lines_as_markdown(lines)
309
+ @footnotes[id] = create_md_element(:footnote, children)
310
+
311
+ end
312
+
313
+
314
+ # This is the only ugly function in the code base.
315
+ # It is used to read list items, descriptions, footnote text
316
+ def read_indented_content(indentation, break_list, item_type)
317
+ lines =[]
318
+ # collect all indented lines
319
+ saw_empty = false; saw_anything_after = false
320
+ while cur_line
321
+ if cur_line_node_type == :empty
322
+ saw_empty = true
323
+ lines << shift_line
324
+ next
325
+ end
326
+
327
+ # after a white line
328
+ if saw_empty
329
+ # we expect things to be properly aligned
330
+ if number_of_leading_spaces(cur_line) < indentation
331
+ # debug "breaking for spaces: #{cur_line}"
332
+ break
333
+ end
334
+ saw_anything_after = true
335
+ else
336
+ break if break_list.include? cur_line_node_type
337
+ # break if cur_line_node_type != :text
338
+ end
339
+
340
+ # debug "Accepted '#{cur_line}'"
341
+
342
+ stripped = strip_indent(shift_line, indentation)
343
+ lines << stripped
344
+
345
+ # You are only required to indent the first line of
346
+ # a child paragraph.
347
+ if line_node_type(stripped) == :text
348
+ while cur_line && (cur_line_node_type == :text)
349
+ lines << strip_indent(shift_line, indentation)
350
+ end
351
+ end
352
+ end
353
+
354
+ want_my_paragraph = saw_anything_after ||
355
+ (saw_empty && (cur_line && (cur_line_node_type == item_type)))
356
+
357
+ # dbg_describe_ary(lines, 'LI')
358
+ # create a new context
359
+
360
+ while lines.last && (line_node_type(lines.last) == :empty)
361
+ lines.pop
362
+ end
363
+
364
+ return lines, want_my_paragraph
365
+ end
366
+
367
+
368
+ def read_quote
369
+ lines = []
370
+ # collect all indented lines
371
+ while cur_line && line_node_type(cur_line) == :quote
372
+ lines << unquote(shift_line)
373
+ end
374
+ # dbg_describe_ary(lines, 'QUOTE')
375
+
376
+ e = create_md_element(:quote)
377
+ e.children = parse_lines_as_markdown(lines)
378
+ e
379
+ end
380
+
381
+ def read_code
382
+ e = create_md_element(:code)
383
+ # collect all indented lines
384
+ lines = []
385
+ while cur_line && ([:code, :empty].include? cur_line_node_type)
386
+ lines << strip_indent(shift_line, 4)
387
+ end
388
+
389
+ while lines.last && (line_node_type(lines.last) == :empty )
390
+ lines.pop
391
+ end
392
+
393
+ return nil if lines.empty?
394
+
395
+ # dbg_describe_ary(lines, 'CODE')
396
+ e.meta[:raw_code] = lines.join("\n")
397
+ e
398
+ end
399
+
400
+ # Reads a series of metadata lines with empty lines in between
401
+ def read_metadata
402
+ hash = {}
403
+ while cur_line
404
+ case cur_line_node_type
405
+ when :empty; shift_line
406
+ when :metadata; hash.merge! parse_metadata(shift_line)
407
+ else break
408
+ end
409
+ end
410
+ hash
411
+ end
412
+
413
+ # parse one metadata line
414
+ def parse_metadata(l)
415
+ hash = {}
416
+ # remove leading '@'
417
+ l = l[1, l.size].strip
418
+ l.split(';').each do |kv|
419
+ k, v = kv.split(':')
420
+ v ||= 'true'
421
+ k = k.strip.to_sym
422
+ hash[k] = v.strip
423
+ end
424
+ hash
425
+ end
426
+
427
+ def read_ref
428
+ line = shift_line
429
+
430
+ # if link is incomplete, shift next line
431
+ while cur_line && (cur_line_node_type != :ref) &&
432
+ ([1,2,3].include? number_of_leading_spaces(cur_line) )
433
+ line += " "+ shift_line
434
+ end
435
+
436
+ # puts "total= #{line}"
437
+
438
+ if match = LinkRegex.match(line)
439
+ id = match[1]; url = match[2]; title = match[3];
440
+ id = id.strip.downcase
441
+
442
+ hash = self.refs[id] = {:url=>url,:title=>title}
443
+
444
+ stuff=match[4]
445
+
446
+ if stuff
447
+ stuff.split.each do |couple|
448
+ # puts "found #{couple}"
449
+ k, v = couple.split('=')
450
+ if v[0,1]=='"' then v = v[1, v.size-2] end
451
+ # puts "key:_#{k}_ value=_#{v}_"
452
+ hash[k.to_sym] = v
453
+ end
454
+ end
455
+ # puts hash.inspect
456
+
457
+ else
458
+ raise "Link does not respect format: '#{line}'"
459
+ end
460
+ end
461
+
462
+ def read_table
463
+
464
+ def split_cells(s)
465
+ s.strip.split('|').select{|x|x.strip.size>0}.map{|x|x.strip}
466
+ end
467
+
468
+ head = split_cells(shift_line).map{|s|
469
+ create_md_element(:head_cell, parse_lines_as_span([s]))}
470
+
471
+ separator=split_cells(shift_line)
472
+
473
+ align = separator.map { |s| s =~ Sep
474
+ if $1 and $2 then :center elsif $2 then :right else :left end }
475
+
476
+ num_columns = align.size
477
+
478
+ if head.size != num_columns
479
+ $stderr.puts "Head does not have #{num_columns} columns: \n#{head.inspect}"
480
+ return create_md_element(:linebreak)
481
+ end
482
+
483
+ rows = []
484
+
485
+ while cur_line && cur_line =~ /\|/
486
+ row = split_cells(shift_line).map{|s|
487
+ create_md_element(:cell, parse_lines_as_span([s]))}
488
+ if head.size != num_columns
489
+ $stderr.puts "Row does not have #{num_columns} columns: \n#{row.inspect}"
490
+ return create_md_element(:linebreak)
491
+ end
492
+ rows << row
493
+ end
494
+
495
+ e = create_md_element(:table)
496
+ e.meta[:align] = align
497
+ e.children = (head+rows).flatten
498
+ e
499
+ end
500
+
501
+ # If current line is text, a definition list is coming
502
+ # if 1) text,empty,[text,empty]*,definition
503
+
504
+ def eventually_comes_a_def_list
505
+ future = create_next_string
506
+ ok = future =~ %r{^t+e?d}x
507
+ # puts "future: #{future} - #{ok}"
508
+ ok
509
+ end
510
+
511
+ # Returns the type of next line as a string
512
+ # breaks at first :definition
513
+ def create_next_string
514
+ s = ""; num_e = 0;
515
+ for line in top
516
+ c = case line_node_type(line)
517
+ when :text; "t"
518
+ when :empty; num_e+=1; "e"
519
+ when :definition; "d"
520
+ else "o"
521
+ end
522
+ s += c
523
+ break if c == "d" or num_e>1
524
+ end
525
+ s
526
+ end
527
+
528
+ def read_definition
529
+ # Read one or more terms
530
+ terms = []
531
+ while cur_line && cur_line_node_type == :text
532
+ terms << create_md_element(:definition_term, parse_lines_as_span([shift_line]))
533
+ end
534
+ # dbg_describe_ary(terms, 'DT')
535
+
536
+ want_paragraph = false
537
+
538
+ raise "Bug!Bug" if not cur_line
539
+
540
+ # one optional empty
541
+ if cur_line_node_type == :empty
542
+ want_my_paragraph = true
543
+ shift_line
544
+ end
545
+
546
+ raise "Bug!Bug" if cur_line_node_type != :definition
547
+
548
+ # Read one or more definitions
549
+ definitions = []
550
+ while cur_line && cur_line_node_type == :definition
551
+ first = shift_line
552
+ first =~ Definition
553
+ first = $1
554
+
555
+ # I know, it's ugly!!!
556
+
557
+ lines, w_m_p =
558
+ read_indented_content(4, [:definition], :definition)
559
+ want_my_paragraph ||= w_m_p
560
+
561
+ lines.unshift first
562
+
563
+ # dbg_describe_ary(lines, 'DD')
564
+
565
+ children = parse_lines_as_markdown(lines)
566
+ definitions << create_md_element(:definition_data, children)
567
+ end
568
+
569
+ definition = create_md_element(:definition)
570
+ definition.meta[:terms] = terms
571
+ definition.meta[:definitions] = definitions
572
+ definition.children = terms + definitions
573
+ definition.meta[:want_my_paragraph] = want_my_paragraph
574
+ definition
575
+ end
576
+ end
577
+