maruku 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (108) hide show
  1. data/bin/{maruku0.3 → marudown} +6 -14
  2. data/bin/maruku +1 -1
  3. data/bin/marutest +37 -9
  4. data/docs/TOFIX.html +22 -0
  5. data/docs/TOFIX.md +3 -0
  6. data/docs/changelog-0.2.13.html +30 -0
  7. data/docs/changelog-0.2.13.md +6 -0
  8. data/docs/changelog-0.3.html +19 -5
  9. data/docs/faq.html +51 -40
  10. data/docs/faq.md +3 -3
  11. data/docs/hidden_o_n_squared.md +10 -0
  12. data/docs/index.html +84 -396
  13. data/docs/markdown_syntax.html +139 -330
  14. data/docs/markdown_syntax.md +80 -93
  15. data/docs/maruku.html +84 -396
  16. data/docs/maruku.md +88 -158
  17. data/docs/proposal.html +13 -106
  18. data/docs/proposal.md +3 -3
  19. data/docs/todo.html +38 -28
  20. data/lib/maruku.rb +77 -11
  21. data/lib/maruku/attributes.rb +186 -0
  22. data/lib/maruku/defaults.rb +40 -0
  23. data/lib/maruku/errors_management.rb +55 -39
  24. data/lib/maruku/helpers.rb +156 -72
  25. data/lib/maruku/input/charsource.rb +319 -0
  26. data/lib/maruku/{html_helper.rb → input/html_helper.rb} +30 -9
  27. data/lib/maruku/input/linesource.rb +111 -0
  28. data/lib/maruku/input/parse_block.rb +562 -0
  29. data/lib/maruku/{parse_doc.rb → input/parse_doc.rb} +60 -28
  30. data/lib/maruku/{parse_span_better.rb → input/parse_span_better.rb} +226 -256
  31. data/lib/maruku/input/type_detection.rb +137 -0
  32. data/lib/maruku/maruku.rb +33 -0
  33. data/lib/maruku/{to_html.rb → output/to_html.rb} +151 -132
  34. data/lib/maruku/{to_latex.rb → output/to_latex.rb} +31 -35
  35. data/lib/maruku/{to_latex_entities.rb → output/to_latex_entities.rb} +25 -3
  36. data/lib/maruku/output/to_latex_strings.rb +64 -0
  37. data/lib/maruku/output/to_markdown.rb +164 -0
  38. data/lib/maruku/{to_s.rb → output/to_s.rb} +6 -0
  39. data/lib/maruku/string_utils.rb +12 -181
  40. data/lib/maruku/structures.rb +91 -67
  41. data/lib/maruku/structures_inspect.rb +78 -0
  42. data/lib/maruku/structures_iterators.rb +24 -2
  43. data/lib/maruku/tests/benchmark.rb +41 -9
  44. data/lib/maruku/tests/new_parser.rb +317 -286
  45. data/lib/maruku/tests/tests.rb +20 -0
  46. data/lib/maruku/toc.rb +64 -64
  47. data/lib/maruku/usage/example1.rb +33 -0
  48. data/lib/maruku/version.rb +8 -2
  49. data/tests/unittest/abbreviations.md +27 -16
  50. data/tests/unittest/attributes/attributes.md +89 -0
  51. data/tests/unittest/attributes/circular.md +51 -0
  52. data/tests/unittest/attributes/default.md +47 -0
  53. data/tests/unittest/blank.md +10 -6
  54. data/tests/unittest/blanks_in_code.md +26 -26
  55. data/tests/unittest/code.md +9 -9
  56. data/tests/unittest/code2.md +12 -13
  57. data/tests/unittest/code3.md +34 -34
  58. data/tests/unittest/easy.md +9 -7
  59. data/tests/unittest/email.md +9 -7
  60. data/tests/unittest/encoding/iso-8859-1.md +41 -4
  61. data/tests/unittest/encoding/utf-8.md +6 -5
  62. data/tests/unittest/entities.md +52 -80
  63. data/tests/unittest/escaping.md +47 -35
  64. data/tests/unittest/extra_dl.md +19 -29
  65. data/tests/unittest/extra_header_id.md +31 -24
  66. data/tests/unittest/extra_table1.md +14 -32
  67. data/tests/unittest/footnotes.md +58 -42
  68. data/tests/unittest/headers.md +11 -11
  69. data/tests/unittest/hrule.md +14 -24
  70. data/tests/unittest/images.md +41 -26
  71. data/tests/unittest/inline_html.md +104 -56
  72. data/tests/unittest/inline_html2.md +38 -0
  73. data/tests/unittest/links.md +74 -33
  74. data/tests/unittest/list1.md +18 -15
  75. data/tests/unittest/list2.md +31 -13
  76. data/tests/unittest/list3.md +29 -28
  77. data/tests/unittest/list4.md +103 -12
  78. data/tests/unittest/lists.md +86 -53
  79. data/tests/unittest/lists6.md +53 -0
  80. data/tests/unittest/lists7.md +31 -0
  81. data/tests/unittest/lists_after_paragraph.md +105 -71
  82. data/tests/unittest/lists_ol.md +149 -73
  83. data/tests/unittest/misc_sw.md +366 -326
  84. data/tests/unittest/notyet/escape.md +10 -10
  85. data/tests/unittest/notyet/header_after_par.md +20 -14
  86. data/tests/unittest/notyet/ticks.md +8 -35
  87. data/tests/unittest/notyet/triggering.md +72 -45
  88. data/tests/unittest/olist.md +78 -0
  89. data/tests/unittest/one.md +5 -3
  90. data/tests/unittest/paragraph.md +5 -3
  91. data/tests/unittest/paragraph_rules/dont_merge_ref.md +15 -9
  92. data/tests/unittest/paragraph_rules/tab_is_blank.md +9 -5
  93. data/tests/unittest/paragraphs.md +21 -26
  94. data/tests/unittest/recover/recover_links.md +6 -5
  95. data/tests/unittest/references/long_example.md +39 -30
  96. data/tests/unittest/references/spaces_and_numbers.md +2 -2
  97. data/tests/unittest/syntax_hl.md +33 -31
  98. data/tests/unittest/test.md +4 -6
  99. data/tests/unittest/wrapping.md +43 -26
  100. metadata +160 -139
  101. data/docs/markdown_extra2.html +0 -87
  102. data/docs/markdown_extra2.md +0 -83
  103. data/docs/markdown_syntax_2.html +0 -152
  104. data/lib/maruku/parse_block.rb +0 -564
  105. data/lib/maruku/parse_span.rb +0 -451
  106. data/lib/maruku/to_latex_strings.rb +0 -59
  107. data/lib/maruku/to_markdown.rb +0 -110
  108. data/lib/test.rb +0 -29
@@ -1,12 +1,32 @@
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
+ #++
1
20
 
2
- class Maruku
21
+
22
+ module MaRuKu; module In; module Markdown; module SpanLevelParser
3
23
 
4
24
  # This class helps me read and sanitize HTML blocks
5
25
 
6
26
  # I tried to do this with REXML, but wasn't able to. (suggestions?)
7
27
 
8
28
  class HTMLHelper
9
- include MarukuStrings
29
+ include MaRuKu::Strings
10
30
 
11
31
  Tag = %r{^<(/)?(\w+)\s*([^>]*)>}m
12
32
  EverythingElse = %r{^[^<]+}m
@@ -69,14 +89,14 @@ class Maruku
69
89
  end
70
90
  elsif is_closing
71
91
  @already += @m.to_s
72
- if @tag_stack.last != tag
73
- error "Malformed: tag <#{tag}> "+
74
- "closes <#{@tag_stack.last}>"
75
- end
76
92
  if @tag_stack.empty?
77
93
  error "Malformed: closing tag #{tag.inspect} "+
78
94
  "in empty list"
79
95
  end
96
+ if @tag_stack.last != tag
97
+ error "Malformed: tag <#{tag}> "+
98
+ "closes <#{@tag_stack.last}>"
99
+ end
80
100
  @tag_stack.pop
81
101
  elsif not is_single
82
102
  @tag_stack.push tag
@@ -98,7 +118,7 @@ class Maruku
98
118
 
99
119
 
100
120
  def error(s)
101
- raise RuntimeError, "Error: #{s} "+ inspect, caller
121
+ raise Exception, "Error: #{s} \n"+ inspect, caller
102
122
  end
103
123
 
104
124
  def inspect; "HTML READER\n comment=#{@inside_comment} "+
@@ -119,5 +139,6 @@ class Maruku
119
139
  def is_finished?
120
140
  not @inside_comment and @tag_stack.empty?
121
141
  end
122
- end
123
- end
142
+ end # html helper
143
+
144
+ end end end end
@@ -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
+
32
+ def initialize(lines, parent=nil, parent_offset=nil)
33
+ raise "NIL lines? " if not lines
34
+ @lines = lines
35
+ @lines_index = 0
36
+ @parent = parent
37
+ @parent_offset = parent_offset
38
+ end
39
+
40
+ def cur_line() @lines[@lines_index] end
41
+ def next_line() @lines[@lines_index+1] end
42
+
43
+ def shift_line()
44
+ raise "Over the rainbow" if @lines_index >= @lines.size
45
+ l = @lines[@lines_index]
46
+ @lines_index += 1
47
+ return l
48
+ end
49
+
50
+ def ignore_line
51
+ raise "Over the rainbow" if @lines_index >= @lines.size
52
+ @lines_index += 1
53
+ end
54
+
55
+ def describe
56
+ #s = "At line ##{@lines_index} of #{@lines.size}:\n"
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|#{l}" %
67
+ [@lines[i].md_type.to_s, prefix]
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,562 @@
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
+ # Splits the string and calls parse_lines_as_markdown
29
+ def parse_text_as_markdown(text)
30
+ lines = split_lines(text)
31
+ src = LineSource.new(lines)
32
+ return parse_blocks(src)
33
+ end
34
+
35
+ def parse_blocks(src)
36
+ output = [];
37
+
38
+ # run state machine
39
+ while src.cur_line
40
+ # Prints detected type (useful for debugging)
41
+ #puts "#{src.cur_line.md_type}|#{src.cur_line}"
42
+ case src.cur_line.md_type
43
+ when :empty;
44
+ src.ignore_line
45
+ when :ial
46
+ src.shift_line =~ /\s*\{([^\}]*)\}\s*/
47
+ al = $1
48
+ al = read_attribute_list(CharSource.new(al), context=nil, break_on=[nil])
49
+ if not output.empty?
50
+ output.last.al = al
51
+ else
52
+ maruku_error "An attribute list at beginning of context {#{al.to_md}}"
53
+ tell_user "I will ignore this AL: {#{al.to_md}}"
54
+ end
55
+ when :ald
56
+ output << read_ald(src)
57
+ when :text
58
+ if src.cur_line =~ MightBeTableHeader and
59
+ (src.next_line && src.next_line =~ TableSeparator)
60
+ output << read_table(src)
61
+ elsif [:header1,:header2].include? src.next_line.md_type
62
+ output << read_header12(src)
63
+ elsif eventually_comes_a_def_list(src)
64
+ definition = read_definition(src)
65
+ if output.last && output.last.node_type == :definition_list
66
+ output.last.children << definition
67
+ else
68
+ output << md_el(:definition_list, [definition])
69
+ end
70
+ else # Start of a paragraph
71
+ output << read_paragraph(src)
72
+ end
73
+ when :header2, :hrule
74
+ # hrule
75
+ src.shift_line
76
+ output << md_hrule()
77
+ when :header3
78
+ output << read_header3(src)
79
+ when :ulist, :olist
80
+ list_type = src.cur_line.md_type == :ulist ? :ul : :ol
81
+ li = read_list_item(src)
82
+ # append to current list if we have one
83
+ if output.last && output.last.node_type == list_type
84
+ output.last.children << li
85
+ else
86
+ output << md_el(list_type, [li])
87
+ end
88
+ when :quote; output << read_quote(src)
89
+ when :code; e = read_code(src); output << e if e
90
+ when :raw_html; e = read_raw_html(src); output << e if e
91
+
92
+ when :footnote_text; output << read_footnote_text(src)
93
+ when :ref_definition; output << read_ref_definition(src)
94
+ when :abbreviation; output << read_abbreviation(src)
95
+
96
+ # # these do not produce output
97
+ when :metadata;
98
+ maruku_error "Please use the new meta-data syntax: \n"+
99
+ " http://maruku.rubyforge.org/proposal.html\n", src
100
+ src.ignore_line
101
+ # warn if we forgot something
102
+ else
103
+ md_type = src.cur_line.md_type
104
+ line = src.cur_line
105
+ maruku_error "Ignoring line '#{line}' type = #{md_type}", src
106
+ src.shift_line
107
+ end
108
+
109
+ # FIXME
110
+ # if current_metadata and output.last
111
+ # output.last.meta.merge! current_metadata
112
+ # current_metadata = nil
113
+ # puts "meta for #{output.last.node_type}\n #{output.last.meta.inspect}"
114
+ # end
115
+ # current_metadata = just_read_metadata
116
+ # just_read_metadata = nil
117
+ end
118
+
119
+ # See for each list if we can omit the paragraphs and use li_span
120
+ # TODO: do this after
121
+ output.each do |c|
122
+ # Remove paragraphs that we can get rid of
123
+ if [:ul,:ol].include? c.node_type
124
+ if c.children.all? {|li| !li.want_my_paragraph} then
125
+ c.children.each do |d|
126
+ d.node_type = :li_span
127
+ d.children = d.children[0].children
128
+ end
129
+ end
130
+ end
131
+ if c.node_type == :definition_list
132
+ if c.children.all?{|defi| !defi.want_my_paragraph} then
133
+ c.children.each do |definition|
134
+ definition.definitions.each do |dd|
135
+ dd.children = dd.children[0].children
136
+ end
137
+ end
138
+ end
139
+ end
140
+ end
141
+
142
+ output
143
+ end
144
+
145
+
146
+ def read_ald(src)
147
+ if (l=src.shift_line) =~ AttributeDefinitionList
148
+ id = $1; al=$2;
149
+ al = read_attribute_list(CharSource.new(al), context=nil, break_on=[nil])
150
+ self.ald[id] = al;
151
+ return md_ald(id, al)
152
+ else
153
+ maruku_error "Bug Bug:\n#{l.inspect}"
154
+ return nil
155
+ end
156
+ end
157
+
158
+ # reads a header (with ----- or ========)
159
+ def read_header12(src)
160
+ line = src.shift_line.strip
161
+ al = nil
162
+ # Check if there is an IAL
163
+ if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
164
+ line = $1.strip
165
+ ial = $2
166
+ al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
167
+ end
168
+ text = parse_lines_as_span [ line ]
169
+ level = src.cur_line.md_type == :header2 ? 2 : 1;
170
+ src.shift_line
171
+ return md_header(level, text, al)
172
+ end
173
+
174
+ # reads a header like '#### header ####'
175
+ def read_header3(src)
176
+ line = src.shift_line.strip
177
+ al = nil
178
+ # Check if there is an IAL
179
+ if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
180
+ line = $1.strip
181
+ ial = $2
182
+ al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
183
+ end
184
+ level = num_leading_hashes(line)
185
+ text = parse_lines_as_span [strip_hashes(line)]
186
+ return md_header(level, text, al)
187
+ end
188
+
189
+
190
+ def read_raw_html(src)
191
+ h = HTMLHelper.new
192
+ begin
193
+ h.eat_this(l=src.shift_line)
194
+ # puts "\nBLOCK:\nhtml -> #{l.inspect}"
195
+ while src.cur_line and not h.is_finished?
196
+ l=src.shift_line
197
+ # puts "html -> #{l.inspect}"
198
+ h.eat_this "\n"+l
199
+ end
200
+ rescue Exception => e
201
+ ex = e.inspect + e.backtrace.join("\n")
202
+ maruku_error "Bad block-level HTML:\n#{add_tabs(ex,1,'|')}\n", src
203
+ end
204
+ raw_html = h.stuff_you_read
205
+ return md_html(raw_html)
206
+ end
207
+
208
+ def read_paragraph(src)
209
+ lines = []
210
+ while src.cur_line
211
+ # :olist does not break
212
+ case t = src.cur_line.md_type
213
+ when :quote,:header3,:empty,:raw_html,:ref_definition,:ial
214
+ break
215
+ when :olist,:ulist
216
+ break if src.next_line.md_type == t
217
+ else
218
+ true
219
+ end
220
+
221
+ break if src.cur_line.strip.size == 0
222
+
223
+ break if [:header1,:header2].include? src.next_line.md_type
224
+
225
+ lines << src.shift_line
226
+ end
227
+ # dbg_describe_ary(lines, 'PAR')
228
+ children = parse_lines_as_span(lines)
229
+
230
+ return md_par(children)
231
+ end
232
+
233
+ # Reads one list item, either ordered or unordered.
234
+ def read_list_item(src)
235
+ parent_offset = src.cur_index
236
+
237
+ item_type = src.cur_line.md_type
238
+ first = src.shift_line
239
+
240
+ # Ugly things going on inside `read_indented_content`
241
+ indentation = spaces_before_first_char(first)
242
+ break_list = [:ulist, :olist, :ial]
243
+ lines, want_my_paragraph =
244
+ read_indented_content(src,indentation, break_list, item_type)
245
+
246
+ # add first line
247
+ # Strip first '*', '-', '+' from first line
248
+ stripped = first[indentation, first.size-1]
249
+ lines.unshift stripped
250
+
251
+ #dbg_describe_ary(lines, 'LIST ITEM ')
252
+
253
+ src2 = LineSource.new(lines, src, parent_offset)
254
+ children = parse_blocks(src2)
255
+ with_par = want_my_paragraph || (children.size>1)
256
+
257
+ return md_li(children, with_par)
258
+ end
259
+
260
+ def read_abbreviation(src)
261
+ if not (l=src.shift_line) =~ Abbreviation
262
+ maruku_error "Bug: it's Andrea's fault. Tell him.\n#{l.inspect}"
263
+ end
264
+
265
+ abbr = $1
266
+ desc = $2
267
+
268
+ if (not abbr) or (abbr.size==0)
269
+ maruku_error "Bad abbrev. abbr=#{abbr.inspect} desc=#{desc.inspect}"
270
+ end
271
+
272
+ self.abbreviations[abbr] = desc
273
+
274
+ return md_abbr_def(abbr, desc)
275
+ end
276
+
277
+ def read_footnote_text(src)
278
+ parent_offset = src.cur_index
279
+
280
+ first = src.shift_line
281
+
282
+ if not first =~ FootnoteText
283
+ maruku_error "Bug (it's Andrea's fault)"
284
+ end
285
+
286
+ id = $1
287
+ text = $2
288
+
289
+ # Ugly things going on inside `read_indented_content`
290
+ indentation = 4 #first.size-text.size
291
+
292
+ # puts "id =_#{id}_; text=_#{text}_ indent=#{indentation}"
293
+
294
+ break_list = [:footnote_text]
295
+ item_type = :footnote_text
296
+ lines, want_my_paragraph =
297
+ read_indented_content(src,indentation, break_list, item_type)
298
+
299
+ # add first line
300
+ if text && text.strip != "" then lines.unshift text end
301
+
302
+ # dbg_describe_ary(lines, 'FOOTNOTE')
303
+ src2 = LineSource.new(lines, src, parent_offset)
304
+ children = parse_blocks(src2)
305
+
306
+ e = md_footnote(id, children)
307
+ self.footnotes[id] = e
308
+ return e
309
+ end
310
+
311
+
312
+ # This is the only ugly function in the code base.
313
+ # It is used to read list items, descriptions, footnote text
314
+ def read_indented_content(src, indentation, break_list, item_type)
315
+ lines =[]
316
+ # collect all indented lines
317
+ saw_empty = false; saw_anything_after = false
318
+ while src.cur_line
319
+ #puts "#{src.cur_line.md_type} #{src.cur_line.inspect}"
320
+ if src.cur_line.md_type == :empty
321
+ saw_empty = true
322
+ lines << src.shift_line
323
+ next
324
+ end
325
+
326
+ # after a white line
327
+ if saw_empty
328
+ # we expect things to be properly aligned
329
+ if (ns=number_of_leading_spaces(src.cur_line)) < indentation
330
+ #puts "breaking for spaces, only #{ns}: #{src.cur_line}"
331
+ break
332
+ end
333
+ saw_anything_after = true
334
+ else
335
+ break if break_list.include? src.cur_line.md_type
336
+ # break if src.cur_line.md_type != :text
337
+ end
338
+
339
+
340
+ stripped = strip_indent(src.shift_line, indentation)
341
+ lines << stripped
342
+
343
+ #puts "Accepted as #{stripped.inspect}"
344
+
345
+ # You are only required to indent the first line of
346
+ # a child paragraph.
347
+ if stripped.md_type == :text
348
+ while src.cur_line && (src.cur_line.md_type == :text)
349
+ lines << strip_indent(src.shift_line, indentation)
350
+ end
351
+ end
352
+ end
353
+
354
+ want_my_paragraph = saw_anything_after ||
355
+ (saw_empty && (src.cur_line && (src.cur_line.md_type == item_type)))
356
+
357
+ # dbg_describe_ary(lines, 'LI')
358
+ # create a new context
359
+
360
+ while lines.last && (lines.last.md_type == :empty)
361
+ lines.pop
362
+ end
363
+
364
+ return lines, want_my_paragraph
365
+ end
366
+
367
+
368
+ def read_quote(src)
369
+ parent_offset = src.cur_index
370
+
371
+ lines = []
372
+ # collect all indented lines
373
+ while src.cur_line && src.cur_line.md_type == :quote
374
+ lines << unquote(src.shift_line)
375
+ end
376
+ # dbg_describe_ary(lines, 'QUOTE')
377
+
378
+ src2 = LineSource.new(lines, src, parent_offset)
379
+ children = parse_blocks(src2)
380
+ return md_quote(children)
381
+ end
382
+
383
+ def read_code(src)
384
+ # collect all indented lines
385
+ lines = []
386
+ while src.cur_line && ([:code, :empty].include? src.cur_line.md_type)
387
+ lines << strip_indent(src.shift_line, 4)
388
+ end
389
+
390
+ #while lines.last && (lines.last.md_type == :empty )
391
+ while lines.last && lines.last.strip.size == 0
392
+ lines.pop
393
+ end
394
+
395
+ while lines.first && lines.first.strip.size == 0
396
+ lines.shift
397
+ end
398
+
399
+ return nil if lines.empty?
400
+
401
+ source = lines.join("\n")
402
+
403
+ # dbg_describe_ary(lines, 'CODE')
404
+
405
+ return md_codeblock(source)
406
+ end
407
+
408
+ # Reads a series of metadata lines with empty lines in between
409
+ def read_metadata(src)
410
+ hash = {}
411
+ while src.cur_line
412
+ case src.cur_line.md_type
413
+ when :empty; src.shift_line
414
+ when :metadata; hash.merge! parse_metadata(src.shift_line)
415
+ else break
416
+ end
417
+ end
418
+ hash
419
+ end
420
+
421
+
422
+ def read_ref_definition(src)
423
+ line = src.shift_line
424
+
425
+ # if link is incomplete, shift next line
426
+ if src.cur_line && (src.cur_line.md_type != :ref_definition) &&
427
+ ([1,2,3].include? number_of_leading_spaces(src.cur_line) )
428
+ line += " "+ src.shift_line
429
+ end
430
+
431
+ # puts "total= #{line}"
432
+
433
+ match = LinkRegex.match(line)
434
+ if not match
435
+ error "Link does not respect format: '#{line}'"
436
+ end
437
+
438
+ id = match[1]; url = match[2]; title = match[3];
439
+ id = id.strip.downcase
440
+
441
+ hash = self.refs[id] = {:url=>url,:title=>title}
442
+
443
+ stuff=match[4]
444
+
445
+ if stuff
446
+ stuff.split.each do |couple|
447
+ # puts "found #{couple}"
448
+ k, v = couple.split('=')
449
+ v ||= ""
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
+ return md_ref_def(id, url, meta={:title=>title})
458
+ end
459
+
460
+ def read_table(src)
461
+
462
+ def split_cells(s)
463
+ s.strip.split('|').select{|x|x.strip.size>0}.map{|x|x.strip}
464
+ end
465
+
466
+ head = split_cells(src.shift_line).map{|s| md_el(:head_cell, parse_lines_as_span([s])) }
467
+
468
+ separator=split_cells(src.shift_line)
469
+
470
+ align = separator.map { |s| s =~ Sep
471
+ if $1 and $2 then :center elsif $2 then :right else :left end }
472
+
473
+ num_columns = align.size
474
+
475
+ if head.size != num_columns
476
+ maruku_error "Table head does not have #{num_columns} columns: \n#{head.inspect}"
477
+ tell_user "I will ignore this table."
478
+ # XXX try to recover
479
+ return md_br()
480
+ end
481
+
482
+ rows = []
483
+
484
+ while src.cur_line && src.cur_line =~ /\|/
485
+ row = split_cells(src.shift_line).map{|s|
486
+ md_el(:cell, parse_lines_as_span([s]))}
487
+ if head.size != num_columns
488
+ maruku_error "Row does not have #{num_columns} columns: \n#{row.inspect}"
489
+ tell_user "I will ignore this table."
490
+ # XXX try to recover
491
+ return md_br()
492
+ end
493
+ rows << row
494
+ end
495
+
496
+ children = (head+rows).flatten
497
+ return md_el(:table, children, {:align => align})
498
+ end
499
+
500
+ # If current line is text, a definition list is coming
501
+ # if 1) text,empty,[text,empty]*,definition
502
+
503
+ def eventually_comes_a_def_list(src)
504
+ future = src.tell_me_the_future
505
+ ok = future =~ %r{^t+e?d}x
506
+ # puts "future: #{future} - #{ok}"
507
+ ok
508
+ end
509
+
510
+
511
+ def read_definition(src)
512
+ # Read one or more terms
513
+ terms = []
514
+ while src.cur_line && src.cur_line.md_type == :text
515
+ terms << md_el(:definition_term, parse_lines_as_span([src.shift_line]))
516
+ end
517
+ # dbg_describe_ary(terms, 'DT')
518
+
519
+ want_my_paragraph = false
520
+
521
+ raise "Chunky Bacon!" if not src.cur_line
522
+
523
+ # one optional empty
524
+ if src.cur_line.md_type == :empty
525
+ want_my_paragraph = true
526
+ src.shift_line
527
+ end
528
+
529
+ raise "Chunky Bacon!" if src.cur_line.md_type != :definition
530
+
531
+ # Read one or more definitions
532
+ definitions = []
533
+ while src.cur_line && src.cur_line.md_type == :definition
534
+ parent_offset = src.cur_index
535
+
536
+ first = src.shift_line
537
+ first =~ Definition
538
+ first = $1
539
+
540
+ # I know, it's ugly!!!
541
+
542
+ lines, w_m_p =
543
+ read_indented_content(src,4, [:definition], :definition)
544
+ want_my_paragraph ||= w_m_p
545
+
546
+ lines.unshift first
547
+
548
+ # dbg_describe_ary(lines, 'DD')
549
+ src2 = LineSource.new(lines, src, parent_offset)
550
+ children = parse_blocks(src2)
551
+ definitions << md_el(:definition_data, children)
552
+ end
553
+
554
+ return md_el(:definition, terms+definitions, {
555
+ :terms => terms,
556
+ :definitions => definitions,
557
+ :want_my_paragraph => want_my_paragraph})
558
+ end
559
+ end # BlockLevelParser
560
+ end # MaRuKu
561
+ end
562
+ end