maruku 0.3.0 → 0.4.0

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 (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,3 +1,4 @@
1
+ #--
1
2
  # Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
2
3
  #
3
4
  # This file is part of Maruku.
@@ -15,34 +16,23 @@
15
16
  # You should have received a copy of the GNU General Public License
16
17
  # along with Maruku; if not, write to the Free Software
17
18
  # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19
+ #++
18
20
 
19
- require 'iconv'
20
21
 
21
- class Maruku
22
- def initialize(s=nil, meta={})
23
- @node_type = :document
24
- @doc = self
22
+ require 'iconv'
25
23
 
26
- @refs = {}
27
- @footnotes = {}
28
- @abbreviations = {}
29
- @meta = meta
30
-
31
- parse_doc(s) if s
32
- end
24
+ module MaRuKu; module In; module Markdown; module BlockLevelParser
33
25
 
34
26
  def parse_doc(s)
35
- # setup initial stack
36
- @stack = []
37
27
 
38
28
  meta2 = parse_email_headers(s)
39
29
  data = meta2[:data]
40
30
  meta2.delete :data
41
31
 
42
- @meta.merge! meta2
32
+ self.attributes.merge! meta2
43
33
 
44
- enc = @meta[:encoding]
45
- @meta.delete :encoding
34
+ enc = self.attributes[:encoding]
35
+ self.attributes.delete :encoding
46
36
  if enc && enc.downcase != 'utf-8'
47
37
  # puts "Converting from #{enc} to UTF-8."
48
38
  converted = Iconv.new('utf-8', enc).iconv(data)
@@ -53,8 +43,7 @@ class Maruku
53
43
  data = converted
54
44
  end
55
45
 
56
- lines = Maruku.split_lines(data)
57
- @children = parse_lines_as_markdown(lines)
46
+ @children = parse_text_as_markdown(data)
58
47
 
59
48
  if true #markdown_extra?
60
49
  self.search_abbreviations
@@ -64,26 +53,70 @@ class Maruku
64
53
  toc = create_toc
65
54
 
66
55
  # use title if not set
67
- if not self.meta[:title] and toc.header_element
56
+ if not self.attributes[:title] and toc.header_element
68
57
  title = toc.header_element.to_s
69
- self.meta[:title] = title
58
+ self.attributes[:title] = title
70
59
  # puts "Set document title to #{title}"
71
60
  end
72
61
 
73
62
  # save for later use
74
63
  self.toc = toc
75
64
 
65
+ # Now do the attributes magic
66
+ each_element do |e|
67
+ # default attribute list
68
+ if default = self.ald[e.node_type.to_s]
69
+ expand_attribute_list(default, e.attributes)
70
+ end
71
+ expand_attribute_list(e.al, e.attributes)
72
+ # puts "#{e.node_type}: #{e.attributes.inspect}"
73
+ end
74
+
76
75
  # puts self.inspect
77
76
  end
77
+
78
+ # Expands an attribute list in an Hash
79
+ def expand_attribute_list(al, result)
80
+ al.each do |k, v|
81
+ case k
82
+ when :class
83
+ if not result[:class]
84
+ result[:class] = v
85
+ else
86
+ result[:class] += " " + v
87
+ end
88
+ when :id; result[:id] = v
89
+ when :ref;
90
+ if self.ald[v]
91
+ already = (result[:expanded_references] ||= [])
92
+ if not already.include?(v)
93
+ already.push v
94
+ expand_attribute_list(self.ald[v], result)
95
+ else
96
+ maruku_error "Circular reference: #{v} already seen\n"+
97
+ already.inspect
98
+ end
99
+ else
100
+ if not result[:unresolved_references]
101
+ result[:unresolved_references] = v
102
+ else
103
+ result[:unresolved_references] << " #{v}"
104
+ end
105
+
106
+ result[v.to_sym] = true
107
+ end
108
+ else
109
+ result[k.to_sym]=v
110
+ end
111
+ end
112
+ end
78
113
 
79
114
  def search_abbreviations
80
- @abbreviations.each do |abbrev, title|
115
+ self.abbreviations.each do |abbrev, title|
81
116
  reg = Regexp.new(Regexp.escape(abbrev))
82
117
  self.replace_each_string do |s|
83
118
  if m = reg.match(s)
84
- e = create_md_element(:abbreviation)
85
- e.children = [abbrev.dup]
86
- e.meta[:title] = title.dup if title
119
+ e = md_abbr(abbrev.dup, title ? title.dup : nil)
87
120
  [m.pre_match, e, m.post_match]
88
121
  else
89
122
  s
@@ -111,7 +144,7 @@ class Maruku
111
144
  e.texts.each do |original_text|
112
145
  # puts "parse_blocks = #{parse_blocks} found = #{original_text} "
113
146
  s = original_text.to_s.strip # XXX
114
- el = create_md_element(:dummy,
147
+ el = md_el(:dummy,
115
148
  parse_blocks ? parse_text_as_markdown(s) :
116
149
  parse_lines_as_span([s]) )
117
150
  el.children_to_html.each do |x|
@@ -125,5 +158,4 @@ class Maruku
125
158
  end
126
159
  end
127
160
 
128
-
129
- end
161
+ end end end end
@@ -1,7 +1,28 @@
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
+
1
22
  require 'set'
2
23
 
3
- class Maruku
4
- include Helpers
24
+ module MaRuKu; module In; module Markdown; module SpanLevelParser
25
+ include MaRuKu::Helpers
5
26
 
6
27
  EscapedCharInText =
7
28
  Set.new [?\\,?`,?*,?_,?{,?},?[,?],?(,?),?#,?.,?!,?|,?:,?+,?-,?>]
@@ -36,8 +57,9 @@ class Maruku
36
57
  while true
37
58
  c = src.cur_char
38
59
 
60
+ # This is only an optimization which cuts 50% of the time used.
61
+ # (but you can't use a-zA-z in exit_on_chars)
39
62
  if c && ((c>=?a && c<=?z) || ((c>=?A && c<=?Z)))
40
- # src.read_text_chars con.cur_string
41
63
  con.cur_string << src.shift_char
42
64
  next
43
65
  end
@@ -49,7 +71,7 @@ class Maruku
49
71
  when ?\ # it's space (32)
50
72
  if src.cur_chars_are " \n"
51
73
  src.ignore_chars(3)
52
- con.push_element create_md_element(:linebreak)
74
+ con.push_element md_br()
53
75
  next
54
76
  else
55
77
  src.ignore_char
@@ -75,20 +97,21 @@ class Maruku
75
97
  else
76
98
  con.push_char src.shift_char
77
99
  end
78
- when ??; read_server_directive
100
+ when ??
101
+ read_server_directive(src, con)
79
102
  when ?\ , ?\t
80
103
  con.push_char src.shift_char
81
- else;
104
+ else
82
105
  if src.next_matches(/<mailto:/) or
83
106
  src.next_matches(/<[\w\.]+\@/)
84
107
  read_email_el(src, con)
85
108
  elsif src.next_matches(/<\w+:/)
86
109
  read_url_el(src, con)
87
110
  elsif src.next_matches(/<\w/)
88
- # puts "This is HTML: #{src.cur_chars(20)}"
111
+ #puts "This is HTML: #{src.cur_chars(20)}"
89
112
  read_inline_html(src, con)
90
113
  else
91
- # puts "This is NOT HTML: #{src.cur_chars(20)}"
114
+ #puts "This is NOT HTML: #{src.cur_chars(20)}"
92
115
  con.push_char src.shift_char
93
116
  end
94
117
  end
@@ -113,15 +136,15 @@ class Maruku
113
136
  con.push_char src.shift_char
114
137
  end
115
138
  when ?&
116
- if m = src.read_regexp(/&([\w\d]+);/)
139
+ if m = src.read_regexp(/\&([\w\d]+);/)
117
140
  con.push_element md_entity(m[1])
118
141
  else
119
142
  con.push_char src.shift_char
120
143
  end
121
144
  when ?*
122
145
  if not src.next_char
123
- error "Opening * as last char", src, con
124
- tell_user "Threating as literal"
146
+ maruku_error "Opening * as last char.", src, con
147
+ maruku_recover "Threating as literal"
125
148
  con.push_char src.shift_char
126
149
  else
127
150
  follows = src.cur_chars(4)
@@ -137,8 +160,8 @@ class Maruku
137
160
  end
138
161
  when ?_
139
162
  if not src.next_char
140
- error "Opening _ as last char", src, con
141
- tell_user "Threating as literal"
163
+ maruku_error "Opening _ as last char", src, con
164
+ maruku_recover "Threating as literal", src, con
142
165
  con.push_char src.shift_char
143
166
  else
144
167
  follows = src.cur_chars(4)
@@ -152,21 +175,84 @@ class Maruku
152
175
  con.push_char src.shift_char
153
176
  end
154
177
  end
178
+ when ?{ # inline attribute list
179
+ if new_meta_data?
180
+ src.ignore_char # opening {
181
+ ial = md_ial(al=read_attribute_list(src, con, [?}]))
182
+ src.ignore_char # closing }
183
+
184
+ con.push_element ial
185
+ else # normal text
186
+ con.push_char src.shift_char
187
+ end
155
188
  when nil
156
- error ("Unclosed span (waiting for %s"+
189
+ maruku_error ("Unclosed span (waiting for %s"+
157
190
  "#{exit_on_strings.inspect})") % [
158
191
  exit_on_chars ? "#{exit_on_chars.inspect} or" : ""],
159
192
  src,con
160
193
 
161
- tell_user "I will boldly go ahead."
162
194
  break
163
195
  else # normal text
164
196
  con.push_char src.shift_char
165
197
  end # end case
166
198
  end # end while true
167
199
  con.push_string_if_present
200
+
201
+ # Now we handle the IAL stuff
202
+ # We need a helper
203
+ def is_ial(e); e.kind_of? MDElement and e.node_type == :ial end
204
+ # A IAL at the beginning is strange and we ignore it
205
+ if false && is_ial(e=con.elements[0])
206
+ "Attribute list at the beginning of span {#{e.to_md}}"
207
+ tell_user "Ignoring {#{e.to_md}}"
208
+ con.elements.shift
209
+ end
210
+ # Apply each IAL to the element before
211
+ con.elements.each_with_index do |e, i| if is_ial(e) && i>= 1 then
212
+ before = con.elements[i-1]
213
+ if before.kind_of? MDElement
214
+ #puts "Assigning #{e.ial} to #{before}"
215
+ before.al = e.ial
216
+ else
217
+ maruku_error "This IAL: {#{e.ial.to_md}} seems to"+
218
+ " refer to a string:\n"+
219
+ before.inspect, src, con
220
+ maruku_recover "Ignoring IAL: {#{e.ial.to_md}}", src, con
221
+ end
222
+ end end
223
+
224
+ # Remove all IAL
225
+ # con.elements.delete_if { |e| is_ial(e) }
226
+
227
+ # Remove leading space
228
+ if (s = con.elements.first).kind_of? String
229
+ if s[0] == ?\ then con.elements[0] = s[1, s.size-1] end
230
+ con.elements.shift if s.size == 0
231
+ end
232
+
233
+ # Remove final spaces
234
+ if (s = con.elements.last).kind_of? String
235
+ s.chop! if s[-1] == ?\
236
+ con.elements.pop if s.size == 0
237
+ end
238
+
168
239
  con.elements
169
240
  end
241
+
242
+ def read_server_directive(src, con)
243
+ delim = "?>"
244
+
245
+ src.ignore_chars delim.size
246
+
247
+ code =
248
+ read_simple(src, escaped=[], break_on_chars=[],
249
+ break_on_strings=[delim])
250
+
251
+ src.ignore_chars delim.size
252
+
253
+ code = (code || "").strip
254
+ con.push_element md_server(code)
255
+ end
170
256
 
171
257
  def read_url_el(src,con)
172
258
  src.ignore_char # leading <
@@ -191,6 +277,9 @@ class Maruku
191
277
  end
192
278
 
193
279
  url = read_simple(src, [], break_on)
280
+ if not url # empty url
281
+ url = ""
282
+ end
194
283
 
195
284
  if url[0] == ?< && url[-1] == ?>
196
285
  url = url[1, url.size-2]
@@ -203,9 +292,19 @@ class Maruku
203
292
  url
204
293
  end
205
294
 
295
+
296
+ def read_quoted_or_unquoted(src, con, escaped, exit_on_chars)
297
+ case src.cur_char
298
+ when ?', ?"
299
+ read_quoted(src, con)
300
+ else
301
+ read_simple(src, escaped, exit_on_chars)
302
+ end
303
+ end
304
+
206
305
  # Tries to read a quoted value. If stream does not
207
306
  # start with ' or ", returns nil.
208
- def read_quoted(src,con)
307
+ def read_quoted(src, con)
209
308
  case src.cur_char
210
309
  when ?', ?"
211
310
  quote_char = src.shift_char # opening quote
@@ -219,8 +318,10 @@ class Maruku
219
318
  end
220
319
 
221
320
  # Reads a simple string (no formatting) until one of break_on_chars,
222
- # while escaping the escaped
223
- def read_simple(src, escaped, exit_on_chars)
321
+ # while escaping the escaped.
322
+ # If the string is empty, it returns nil.
323
+ # Raises on error.
324
+ def read_simple(src, escaped, exit_on_chars, exit_on_strings=nil)
224
325
  text = ""
225
326
  while true
226
327
  # puts "Reading simple #{text.inspect}"
@@ -229,12 +330,17 @@ class Maruku
229
330
  # puts (" breaking on "<<c)+" contained in "+exit_on_chars.inspect
230
331
  break
231
332
  end
333
+
334
+ break if exit_on_strings &&
335
+ exit_on_strings.any? {|x| src.cur_chars_are x}
336
+
232
337
  case c
233
338
  when nil
234
- s= "String finished while reading (break on #{exit_on_chars.inspect})"+
339
+ s= "String finished while reading (break on "+
340
+ "#{exit_on_chars.map{|x|""<<x}.inspect})"+
235
341
  " already read: #{text.inspect}"
236
- error s, src
237
- tell_user "I boldly continue"
342
+ maruku_error s, src
343
+ maruku_recover "I boldly continue", src, con
238
344
  break
239
345
  when ?\\
240
346
  d = src.next_char
@@ -249,7 +355,7 @@ class Maruku
249
355
  end
250
356
  end
251
357
  # puts "Read simple #{text.inspect}"
252
- text
358
+ text.empty? ? nil : text
253
359
  end
254
360
 
255
361
  def read_em(src, delim)
@@ -321,48 +427,33 @@ class Maruku
321
427
  maruku_error "Bad html: \n" +
322
428
  add_tabs(e.inspect+e.backtrace.join("\n"),1,'>'),
323
429
  src,con
324
- tell_user "I will try to continue after bad HTML."
430
+ maruku_recover "I will try to continue after bad HTML.", src, con
325
431
  con.push_char src.shift_char
326
432
  end
327
433
  end
328
434
 
329
435
  def read_inline_code(src, con)
436
+ # Count the number of ticks
330
437
  num_ticks = 0
331
-
332
438
  while src.cur_char == ?`
333
439
  num_ticks += 1
334
440
  src.ignore_char
335
441
  end
442
+ # We will read until this string
443
+ end_string = "`"*num_ticks
336
444
 
445
+ code =
446
+ read_simple(src, escaped=[], break_on_chars=[],
447
+ break_on_strings=[end_string])
337
448
 
338
- # ignore space
339
- if num_ticks > 1 && src.cur_char == SPACE
340
- src.ignore_char
341
- end
342
-
343
- # puts "Ticks: #{num_ticks } next: #{src.some} "
344
-
345
- end_string = "`"*num_ticks
449
+ # puts "Now I expects #{num_ticks} ticks: #{src.cur_chars(10).inspect}"
450
+ src.ignore_chars num_ticks
346
451
 
347
- code = ''
348
- while true
349
- if not src.cur_char
350
- error("Ticks not finished: read #{code.inspect}"+
351
- " and waiting for #{end_string.inspect} num=#{num_ticks}",
352
- src,con)
353
- tell_user "Read invalid code block: #{code.inspect}"
354
- break
355
- end
356
-
357
- if src.cur_chars(num_ticks) ==end_string # bah
358
- # puts "Breaking on #{src.some} (end:#{end_string.inspect})"
359
- src.ignore_chars num_ticks
360
- break
361
- end
362
-
363
- code << src.shift_char
452
+ # Ignore at most one space
453
+ if num_ticks > 1 && code[0] == SPACE
454
+ code = code[1, code.size-1]
364
455
  end
365
-
456
+
366
457
  # drop last space
367
458
  if num_ticks > 1 && code[-1] == SPACE
368
459
  code = code[0,code.size-1]
@@ -371,17 +462,6 @@ class Maruku
371
462
  # puts "Read `` code: #{code.inspect}; after: #{src.cur_chars(10).inspect} "
372
463
  con.push_element md_code(code)
373
464
  end
374
-
375
-
376
-
377
- def read_server_directive
378
- # match = gimme(/^(.*)\?>/)
379
- # if not match
380
- # error "Server directive not closed"
381
- # end
382
- # server = match[1]
383
- # con.found_object create_md_element(:server, server)
384
- end
385
465
 
386
466
  def read_link(src, con)
387
467
  # we read the string and see what happens
@@ -405,14 +485,31 @@ class Maruku
405
485
  src.consume_whitespace
406
486
  title = nil
407
487
  if src.cur_char != ?) # we have a title
488
+ quote_char = src.cur_char
408
489
  title = read_quoted(src,con)
490
+
491
+ if not title
492
+ maruku_error 'Must quote title',src,con
493
+ else
494
+ # Tries to read a title with quotes: ![a](url "ti"tle")
495
+ # this is the most ugly thing in Markdown
496
+ if not src.next_matches(/\s*\)/)
497
+ # if there is not a closing par ), then read
498
+ # the rest and guess it's title with quotes
499
+ rest = read_simple(src, escaped=[], break_on_chars=[?)],
500
+ break_on_strings=[])
501
+ # chop the closing char
502
+ rest.chop!
503
+ title << quote_char << rest
504
+ end
505
+ end
409
506
  end
410
507
  src.consume_whitespace
411
508
  closing = src.shift_char # closing )
412
509
  if closing != ?)
413
- error 'Unclosed link',src,con
414
- tell_user "No closing ): I will not create"+
415
- " the link for #{children.inspect}"
510
+ maruku_error 'Unclosed link',src,con
511
+ maruku_recover "No closing ): I will not create"+
512
+ " the link for #{children.inspect}", src, con
416
513
  con.push_elements children
417
514
  return
418
515
  end
@@ -423,7 +520,8 @@ class Maruku
423
520
  con.push_element md_link(children, ref_id)
424
521
  else
425
522
  maruku_error "Could not read ref_id", src, con
426
- tell_user "I will not create the link for #{children.inspect}"
523
+ maruku_recover "I will not create the link for "+
524
+ "#{children.inspect}", src, con
427
525
  con.push_elements children
428
526
  return
429
527
  end
@@ -453,8 +551,23 @@ class Maruku
453
551
  src.consume_whitespace
454
552
  title = nil
455
553
  if src.cur_char != ?) # we have a title
554
+ quote_char = src.cur_char
456
555
  title = read_quoted(src,con)
457
- error 'Must quote title',src,con if not title
556
+ if not title
557
+ maruku_error 'Must quote title',src,con
558
+ else
559
+ # Tries to read a title with quotes: ![a](url "ti"tle")
560
+ # this is the most ugly thing in Markdown
561
+ if not src.next_matches(/\s*\)/)
562
+ # if there is not a closing par ), then read
563
+ # the rest and guess it's title with quotes
564
+ rest = read_simple(src, escaped=[], break_on_chars=[?)],
565
+ break_on_strings=[])
566
+ # chop the closing char
567
+ rest.chop!
568
+ title << quote_char << rest
569
+ end
570
+ end
458
571
  end
459
572
  src.consume_whitespace
460
573
  closing = src.shift_char # closing )
@@ -471,211 +584,68 @@ class Maruku
471
584
  end
472
585
  end # read link
473
586
 
474
- end
475
-
476
587
 
477
- class SpanContext
478
- include MarukuStrings
588
+ class SpanContext
589
+ include MaRuKu::Strings
479
590
 
480
- # Read elements
481
- attr_accessor :elements
482
- attr_accessor :cur_string
483
-
484
- def initialize
485
- @elements = []
486
- @cur_string = ""
487
- end
591
+ # Read elements
592
+ attr_accessor :elements
593
+ attr_accessor :cur_string
488
594
 
489
- def push_element(e)
490
- raise "Only MDElement and String, please. You pushed #{e.class}: #{e.inspect} " if
491
- not (e.kind_of?(String) or e.kind_of?(MDElement))
492
-
493
- push_string_if_present
494
- @elements << e
495
- nil
496
- end
497
-
498
- def push_elements(a)
499
- for e in a
500
- if e.kind_of? String
501
- e.each_byte do |b| push_char b end
502
- else
503
- push_element e
504
- end
505
- end
506
- end
507
- def push_string_if_present
508
- if @cur_string.size > 0
509
- @elements << @cur_string
595
+ def initialize
596
+ @elements = []
510
597
  @cur_string = ""
511
598
  end
512
- nil
513
- end
514
-
515
- def push_char(c)
516
- @cur_string << c
517
- nil
518
- end
519
-
520
- # push space into current string if
521
- # there isn't one
522
- def push_space
523
- last = @cur_string[@cur_string.size-1]
524
- @cur_string << ?\ if last != ?\
525
- end
526
599
 
527
- def describe
528
- lines = @elements.map{|x| x.inspect}.join("\n")
529
- s = "Elements read in span: \n" +
530
- add_tabs(lines,1, ' -')+"\n"
600
+ def push_element(e)
601
+ raise "Only MDElement and String, please. You pushed #{e.class}: #{e.inspect} " if
602
+ not (e.kind_of?(String) or e.kind_of?(MDElement))
531
603
 
532
- if @cur_string.size > 0
533
- s += "Current string: \n #{@cur_string.inspect}\n"
604
+ push_string_if_present
605
+ @elements << e
606
+ nil
534
607
  end
535
- s
536
- end
537
-
538
- end
539
-
540
- class CharSource
541
- include MarukuStrings
542
-
543
- def initialize(s)
544
- @elements = []
545
- @cur_string = ""
546
- @buffer = s
547
- @buffer_index = 0
548
- end
549
-
550
- # Return current char as a FixNum (or nil).
551
- def cur_char; @buffer[@buffer_index] end
552
608
 
553
- # Return the next n chars as a String.
554
- def cur_chars(n); @buffer[@buffer_index,n] end
555
-
556
- # Return the char after current char as a FixNum (or nil).
557
- def next_char; @buffer[@buffer_index+1] end
558
-
559
- def shift_char
560
- c = @buffer[@buffer_index]
561
- @buffer_index+=1
562
- c
563
- end
564
-
565
- def ignore_char
566
- @buffer_index+=1
567
- end
568
-
569
- def ignore_chars(n)
570
- @buffer_index+=n
571
- nil
572
- end
573
-
574
- def current_remaining_buffer
575
- @buffer[@buffer_index, @buffer.size-@buffer_index]
576
- end
577
-
578
- def cur_chars_are(string)
579
- r2 = /^.{#{@buffer_index}}#{Regexp.escape string}/m
580
- @buffer =~ r2
581
- end
582
-
583
- def next_matches(r)
584
- r2 = /^.{#{@buffer_index}}#{r}/m
585
- r2.match @buffer
586
- end
587
-
588
- def read_regexp(r)
589
- r2 = /^.{#{@buffer_index}}#{r}/m
590
- m = r2.match @buffer
591
- if m
592
- consumed = m.to_s.size - @buffer_index
593
- # puts "Consumed #{consumed} chars (entire is #{m.to_s.inspect})"
594
- ignore_chars consumed
595
- else
596
- # puts "Could not read regexp #{r2.inspect} from buffer "+
597
- # " index=#{@buffer_index}"
598
- # puts "Cur chars = #{cur_chars(20).inspect}"
599
- # puts "Matches? = #{cur_chars(20) =~ r}"
609
+ def push_elements(a)
610
+ for e in a
611
+ if e.kind_of? String
612
+ e.each_byte do |b| push_char b end
613
+ else
614
+ push_element e
615
+ end
616
+ end
600
617
  end
601
- m
602
- end
603
-
604
- def consume_whitespace
605
- while c = cur_char
606
- if (c == 32 || c == ?\t)
607
- # puts "ignoring #{c}"
608
- ignore_char
609
- else
610
- # puts "#{c} is not ws: "<<c
611
- break
618
+ def push_string_if_present
619
+ if @cur_string.size > 0
620
+ @elements << @cur_string
621
+ @cur_string = ""
612
622
  end
623
+ nil
613
624
  end
614
- end
615
-
616
- def read_text_chars(out)
617
- s = @buffer.size; c=nil
618
- while @buffer_index < s && (c=@buffer[@buffer_index]) &&
619
- ((c>=?a && c<=?z) || (c>=?A && c<=?Z))
620
- out << c
621
- @buffer_index += 1
625
+
626
+ def push_char(c)
627
+ @cur_string << c
628
+ nil
622
629
  end
623
- end
624
630
 
625
- def describe
626
-
627
- len = 75
628
- num_before = [len/2, @buffer_index].min
629
- num_after = [len/2, @buffer.size-@buffer_index].min
630
- num_before_max = @buffer_index
631
- num_after_max = @buffer.size-@buffer_index
632
-
633
- # puts "num #{num_before} #{num_after}"
634
- num_before = [num_before_max, len-num_after].min
635
- num_after = [num_after_max, len-num_before].min
636
- # puts "num #{num_before} #{num_after}"
637
-
638
- index_start = [@buffer_index - num_before, 0].max
639
- index_end = [@buffer_index + num_after, @buffer.size].min
640
-
641
- size = index_end- index_start
642
-
643
- # puts "- #{index_start} #{size}"
644
-
645
- str = @buffer[index_start, size]
646
- str.gsub!("\n",'N')
647
- str.gsub!("\t",'T')
648
-
649
- if index_end == @buffer.size
650
- str += "EOF"
631
+ # push space into current string if
632
+ # there isn't one
633
+ def push_space
634
+ last = @cur_string[@cur_string.size-1]
635
+ @cur_string << ?\ if last != ?\
651
636
  end
652
-
653
- pre_s = @buffer_index-index_start
654
- pre_s = [pre_s, 0].max
655
- pre_s2 = [len-pre_s,0].max
656
- # puts "pre_S = #{pre_s}"
657
- pre =" "*(pre_s)
658
-
659
- "-"*len+"\n"+
660
- str + "\n" +
661
- "-"*pre_s + "|" + "-"*(pre_s2)+"\n"+
662
- # pre + "|\n"+
663
- pre + "+--- Byte #{@buffer_index}\n"+
664
-
665
-
666
- "Shown bytes [#{index_start} to #{size}] of #{@buffer.size}:\n"+
667
- add_tabs(@buffer,1,">")
637
+
638
+ def describe
639
+ lines = @elements.map{|x| x.inspect}.join("\n")
640
+ s = "Elements read in span: \n" +
641
+ add_tabs(lines,1, ' -')+"\n"
668
642
 
669
- # "CharSource: At character #{@buffer_index} of block "+
670
- # " beginning with:\n #{@buffer[0,50].inspect} ...\n"+
671
- # " before: \n ... #{cur_chars(50).inspect} ... "
672
- end
643
+ if @cur_string.size > 0
644
+ s += "Current string: \n #{@cur_string.inspect}\n"
645
+ end
646
+ s
647
+ end
648
+ end # SpanContext
673
649
 
674
- def some
675
- cur_chars(15).inspect
676
- end
677
- end
678
-
679
-
680
-
650
+ end end end end # module MaRuKu; module In; module Markdown; module SpanLevelParser
681
651