maruku 0.7.1 → 0.7.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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/lib/maruku.rb +0 -1
- data/lib/maruku/errors.rb +10 -1
- data/lib/maruku/input/mdline.rb +1 -1
- data/lib/maruku/input/parse_span.rb +90 -7
- data/lib/maruku/output/to_html.rb +1 -1
- data/lib/maruku/version.rb +1 -1
- data/spec/block_docs/issue126.md +9 -0
- data/spec/block_docs/issue130.md +11 -0
- data/spec/span_spec.rb +7 -0
- metadata +20 -19
- metadata.gz.sig +0 -0
- data/lib/maruku/input/rubypants.rb +0 -310
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 708a01507319b4ce86bcd733bb4767009aea05c5
|
4
|
+
data.tar.gz: 34c0dd886c8f800e2746dec49d9e2b2d71823270
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: faafb8314908e9c5edecc191e06ece6ad7fac71f6f840091c27d20bbbee0f1017f8f3dbeaa21e385561b60e049fb6d50c75c52e886b26695e896da76bb9ac523
|
7
|
+
data.tar.gz: dfde96cf9d35055eb1dbece3753ddc57d6b12db104b00fed643b57994a59ee6744524232fc0cf13d3db9976821566ba02ab2ede77a9f04cd4e459a3de308c86b
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data.tar.gz.sig
CHANGED
Binary file
|
data/lib/maruku.rb
CHANGED
data/lib/maruku/errors.rb
CHANGED
@@ -35,8 +35,17 @@ module MaRuKu
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
# This is like {#maruku_error} but will never raise.
|
38
39
|
def maruku_recover(s, src=nil, con=nil, recover=nil)
|
39
|
-
|
40
|
+
policy = get_setting(:on_error)
|
41
|
+
|
42
|
+
case policy
|
43
|
+
when :ignore
|
44
|
+
when :raise, :warning
|
45
|
+
tell_user create_frame(describe_error(s, src, con, recover))
|
46
|
+
else
|
47
|
+
raise "Unknown on_error policy: #{policy.inspect}"
|
48
|
+
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def raise_error(s)
|
data/lib/maruku/input/mdline.rb
CHANGED
@@ -41,7 +41,7 @@ module MaRuKu
|
|
41
41
|
# I had a bug with emails and urls at the beginning of the
|
42
42
|
# line that were mistaken for raw_html
|
43
43
|
return :text if self =~ /\A[ ]{0,3}#{EMailAddress}/
|
44
|
-
return :text if self =~ /\A[ ]{0,3}
|
44
|
+
return :text if self =~ /\A[ ]{0,3}<\w+:\/\//
|
45
45
|
# raw html is like PHP Markdown Extra: at most three spaces before
|
46
46
|
return :xml_instr if self =~ /\A\s*<\?/
|
47
47
|
return :raw_html if self =~ %r{\A[ ]{0,3}</?\s*\w+}
|
@@ -22,14 +22,16 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
22
22
|
def read_span(src, escaped, exit_on_chars=nil, exit_on_strings=nil)
|
23
23
|
escaped = Array(escaped)
|
24
24
|
con = SpanContext.new
|
25
|
-
|
25
|
+
dquote_state = squote_state = :closed
|
26
|
+
c = d = prev_char = nil
|
26
27
|
while true
|
27
28
|
c = src.cur_char
|
28
29
|
|
29
30
|
# This is only an optimization which cuts 50% of the time used.
|
30
31
|
# (but you can't use a-zA-z in exit_on_chars)
|
31
|
-
if c && c =~
|
32
|
+
if c && c =~ /[[:alnum:]]/
|
32
33
|
con.push_char src.shift_char
|
34
|
+
prev_char = c
|
33
35
|
next
|
34
36
|
end
|
35
37
|
|
@@ -49,7 +51,21 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
49
51
|
if src.cur_chars_are " \n"
|
50
52
|
src.ignore_chars(3)
|
51
53
|
con.push_element md_br
|
54
|
+
prev_char = ' '
|
52
55
|
next
|
56
|
+
elsif src.cur_chars_are ' >>' # closing guillemettes
|
57
|
+
src.ignore_chars(3)
|
58
|
+
con.push_element md_entity('nbsp')
|
59
|
+
con.push_element md_entity('raquo')
|
60
|
+
elsif src.cur_chars(5) =~ / '\d\ds/ # special case: '80s
|
61
|
+
src.ignore_chars(2)
|
62
|
+
con.push_space
|
63
|
+
con.push_element md_entity('rsquo')
|
64
|
+
elsif src.cur_chars_are " '" # opening single-quote
|
65
|
+
src.ignore_chars(2)
|
66
|
+
con.push_space
|
67
|
+
con.push_element md_entity('lsquo')
|
68
|
+
squote_state = :open
|
53
69
|
else
|
54
70
|
src.ignore_char
|
55
71
|
con.push_space
|
@@ -70,9 +86,14 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
70
86
|
|
71
87
|
case d = src.next_char
|
72
88
|
when '<' # guillemettes
|
73
|
-
src.
|
74
|
-
|
75
|
-
|
89
|
+
if src.cur_chars_are '<< '
|
90
|
+
src.ignore_chars(3)
|
91
|
+
con.push_element md_entity('laquo')
|
92
|
+
con.push_element md_entity('nbsp')
|
93
|
+
else
|
94
|
+
src.ignore_chars(2)
|
95
|
+
con.push_element md_entity('laquo')
|
96
|
+
end
|
76
97
|
when '!'
|
77
98
|
if src.cur_chars_are '<!--'
|
78
99
|
read_inline_html(src, con)
|
@@ -97,6 +118,13 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
97
118
|
con.push_char src.shift_char
|
98
119
|
end
|
99
120
|
end
|
121
|
+
when '>'
|
122
|
+
if src.next_char == '>'
|
123
|
+
src.ignore_chars(2)
|
124
|
+
con.push_element md_entity('raquo')
|
125
|
+
else
|
126
|
+
con.push_char src.shift_char
|
127
|
+
end
|
100
128
|
when "\\"
|
101
129
|
d = src.next_char
|
102
130
|
if d == "'"
|
@@ -195,9 +223,60 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
195
223
|
[ exit_on_chars ? "#{exit_on_chars.inspect} or" : "" ],
|
196
224
|
src, con)
|
197
225
|
break
|
226
|
+
when '-' # dashes
|
227
|
+
if src.next_char == '-'
|
228
|
+
if src.cur_chars_are '---'
|
229
|
+
src.ignore_chars(3)
|
230
|
+
con.push_element md_entity('mdash')
|
231
|
+
else
|
232
|
+
src.ignore_chars(2)
|
233
|
+
con.push_element md_entity('ndash')
|
234
|
+
end
|
235
|
+
else
|
236
|
+
con.push_char src.shift_char
|
237
|
+
end
|
238
|
+
when '.' # ellipses
|
239
|
+
if src.cur_chars_are '...'
|
240
|
+
src.ignore_chars(3)
|
241
|
+
con.push_element md_entity('hellip')
|
242
|
+
elsif src.cur_chars_are '. . .'
|
243
|
+
src.ignore_chars(5)
|
244
|
+
con.push_element md_entity('hellip')
|
245
|
+
else
|
246
|
+
con.push_char src.shift_char
|
247
|
+
end
|
248
|
+
when '"'
|
249
|
+
if dquote_state == :closed
|
250
|
+
dquote_state = :open
|
251
|
+
src.ignore_char
|
252
|
+
con.push_element md_entity('ldquo')
|
253
|
+
else
|
254
|
+
dquote_state = :closed
|
255
|
+
src.ignore_char
|
256
|
+
con.push_element md_entity('rdquo')
|
257
|
+
end
|
258
|
+
when "'"
|
259
|
+
if src.cur_chars(4) =~ /'\d\ds/ # special case: '80s
|
260
|
+
src.ignore_char
|
261
|
+
con.push_element md_entity('rsquo')
|
262
|
+
elsif squote_state == :open
|
263
|
+
squote_state = :closed unless src.next_char =~ /[[:alpha:]]/
|
264
|
+
src.ignore_char
|
265
|
+
con.push_element md_entity('rsquo')
|
266
|
+
else
|
267
|
+
if prev_char =~ /[[:alpha:]]/
|
268
|
+
src.ignore_char
|
269
|
+
con.push_element md_entity('rsquo')
|
270
|
+
else
|
271
|
+
src.ignore_char
|
272
|
+
con.push_element md_entity('lsquo')
|
273
|
+
squote_state = :open
|
274
|
+
end
|
275
|
+
end
|
198
276
|
else # normal text
|
199
277
|
con.push_char src.shift_char
|
200
278
|
end # end case
|
279
|
+
prev_char = c
|
201
280
|
end # end while true
|
202
281
|
|
203
282
|
con.push_string_if_present
|
@@ -212,6 +291,8 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
212
291
|
end
|
213
292
|
con.elements.shift if s.empty?
|
214
293
|
end
|
294
|
+
|
295
|
+
con.elements.shift if (con.elements.first.kind_of?(String) && con.elements.first.empty?)
|
215
296
|
|
216
297
|
# Remove final spaces
|
217
298
|
if (s = con.elements.last).kind_of? String
|
@@ -219,7 +300,7 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
219
300
|
con.elements.pop if s.empty?
|
220
301
|
end
|
221
302
|
|
222
|
-
|
303
|
+
con.elements
|
223
304
|
end
|
224
305
|
|
225
306
|
|
@@ -449,7 +530,9 @@ module MaRuKu::In::Markdown::SpanLevelParser
|
|
449
530
|
|
450
531
|
# Try to handle empty single-ticks
|
451
532
|
if num_ticks > 1 && !src.next_matches(/.*#{Regexp.escape(end_string)}/)
|
452
|
-
con.push_element(
|
533
|
+
con.push_element md_entity('ldquo')
|
534
|
+
src.ignore_chars(2)
|
535
|
+
return
|
453
536
|
end
|
454
537
|
|
455
538
|
code = read_simple(src, nil, nil, end_string)
|
data/lib/maruku/version.rb
CHANGED
@@ -0,0 +1,9 @@
|
|
1
|
+
Handle HTTPS links at the start of a line. https://github.com/bhollis/maruku/issues/126
|
2
|
+
*** Parameters: ***
|
3
|
+
{ }
|
4
|
+
*** Markdown input: ***
|
5
|
+
<https://google.com>
|
6
|
+
*** Output of inspect ***
|
7
|
+
|
8
|
+
*** Output of to_html ***
|
9
|
+
<p><a href="https://google.com">https://google.com</a></p>
|
@@ -0,0 +1,11 @@
|
|
1
|
+
Handle ellipsis at the end of a line. https://github.com/bhollis/maruku/issues/130
|
2
|
+
*** Parameters: ***
|
3
|
+
{ }
|
4
|
+
*** Markdown input: ***
|
5
|
+
A paragraph... continued...
|
6
|
+
*** Output of inspect ***
|
7
|
+
md_el(:document, md_par(["A paragraph", md_entity("hellip"), " continued", md_entity("hellip")]))
|
8
|
+
*** Output of to_html ***
|
9
|
+
<p>A paragraph… continued…</p>
|
10
|
+
*** Output of to_latex ***
|
11
|
+
A paragraph\ldots{} continued\ldots{}
|
data/spec/span_spec.rb
CHANGED
@@ -162,6 +162,11 @@ EXPECTATIONS = Maruku.new.instance_eval do
|
|
162
162
|
["[a](#url)", [md_im_link(['a'],'#url')]],
|
163
163
|
["[a](</script?foo=1&bar=2>)", [md_im_link(['a'],'/script?foo=1&bar=2')]],
|
164
164
|
|
165
|
+
# Links to URLs that contain closing parentheses. #128
|
166
|
+
['[a](url())', [md_im_link(['a'],'url()')], 'Link with parentheses 1', true], # PENDING
|
167
|
+
['[a](url\(\))', [md_im_link(['a'],'url()')], 'Link with parentheses 2', true], # PENDING
|
168
|
+
['[a](url()foo)', [md_im_link(['a'],'url()foo')], 'Link with parentheses 3', true], # PENDING
|
169
|
+
['[a](url(foo))', [md_im_link(['a'],'url(foo)')], 'Link with parentheses 4', true], # PENDING
|
165
170
|
|
166
171
|
# Images
|
167
172
|
["\\", ['!', md_im_link(['a'],'url') ], 'Escaping images'],
|
@@ -186,6 +191,8 @@ EXPECTATIONS = Maruku.new.instance_eval do
|
|
186
191
|
|
187
192
|
['<http://example.com/?foo=1&bar=2>',
|
188
193
|
[md_url('http://example.com/?foo=1&bar=2')], 'Immediate link'],
|
194
|
+
['<https://example.com/?foo=1&bar=2>',
|
195
|
+
[md_url('https://example.com/?foo=1&bar=2')], 'Immediate link https'],
|
189
196
|
['a<http://example.com/?foo=1&bar=2>b',
|
190
197
|
['a',md_url('http://example.com/?foo=1&bar=2'),'b'] ],
|
191
198
|
['<andrea@censi.org>',
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: maruku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrea Censi
|
@@ -14,25 +14,25 @@ cert_chain:
|
|
14
14
|
-----BEGIN CERTIFICATE-----
|
15
15
|
MIIDbDCCAlSgAwIBAgIBATANBgkqhkiG9w0BAQUFADA+MQwwCgYDVQQDDANiZW4x
|
16
16
|
GTAXBgoJkiaJk/IsZAEZFgliZW5ob2xsaXMxEzARBgoJkiaJk/IsZAEZFgNuZXQw
|
17
|
-
|
17
|
+
HhcNMTQwNTI2MjExNjI2WhcNMTUwNTI2MjExNjI2WjA+MQwwCgYDVQQDDANiZW4x
|
18
18
|
GTAXBgoJkiaJk/IsZAEZFgliZW5ob2xsaXMxEzARBgoJkiaJk/IsZAEZFgNuZXQw
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCYZvEbHldtIRP8nQNyM6SQ
|
20
|
+
oqi6pF1VznSiOzVIZi84MnTwab4jl4pEso/tnjpm+jpHoFc1RhJMwwaO9v0ih5/e
|
21
|
+
VRZFZQyoKrPaP9Dq3q1iO9SXwoucGfoVCcvtfF2DGnFlkDZlswPWNdL/GTxuiM6X
|
22
|
+
dzQ36hzSSZrgTms1AdPSuCHt3LTNRDSkpRGqDWdsPbKLi7eLSkGbUO1Ibe8JAtdE
|
23
|
+
ueSaVAksFJvNgUxZHMSBkSrd33PRpiSi2g6IvogoeWGj+4vxJgGbPGGWHmsfVT28
|
24
|
+
ggEdA8Ix8y223pCovPUJtCEoEdoNxTq7NUA9lUL2rZ0URc2jCiEKzQmtRAf8ZWSn
|
25
|
+
AgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYDVR0PBAQDAgSwMB0GA1UdDgQWBBSL7ijr
|
26
|
+
kd/8I5COGX6gWvHNSCPyTDAcBgNVHREEFTATgRFiZW5AYmVuaG9sbGlzLm5ldDAc
|
27
27
|
BgNVHRIEFTATgRFiZW5AYmVuaG9sbGlzLm5ldDANBgkqhkiG9w0BAQUFAAOCAQEA
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
28
|
+
PHS/jYQNXccqlZVJUVz5hlkXCslHZs8OqfzOIPRQ4VZZY/USERfDCHou8eQRxeOG
|
29
|
+
Ux9/jfp35TQRC/1OEx/7mDhixRo3vYCdHHUUUKOdko9VWSyTrTmVLq6V1Iwu7TCe
|
30
|
+
K+yasGZ4CfUqUaK+MgFLEEI8k0q2TmRd534a3C6nGS69x9HmIqJISlpvwNYr1YCX
|
31
|
+
mk4SFXYX0a5PWeGRiIKg4vPQy4PG1oFAN7+mAgSGNRtMG3Sx4qkMaYLfW0wd7zZ9
|
32
|
+
IjHSEqEpekJnAXUJNPdgIBHUVUMNfcnULDPNzaBckgjGm0PqFMlknEOk+NxoXt7m
|
33
|
+
ouF3Zkp3xx1U+2uMJ1SVRg==
|
34
34
|
-----END CERTIFICATE-----
|
35
|
-
date: 2014-
|
35
|
+
date: 2014-05-26 00:00:00.000000000 Z
|
36
36
|
dependencies: []
|
37
37
|
description: "Maruku is a Markdown interpreter in Ruby.\n\tIt features native export
|
38
38
|
to HTML and PDF (via Latex). The\n\toutput is really beautiful!"
|
@@ -81,7 +81,6 @@ files:
|
|
81
81
|
- lib/maruku/input/parse_block.rb
|
82
82
|
- lib/maruku/input/parse_doc.rb
|
83
83
|
- lib/maruku/input/parse_span.rb
|
84
|
-
- lib/maruku/input/rubypants.rb
|
85
84
|
- lib/maruku/input_textile2/t2_parser.rb
|
86
85
|
- lib/maruku/inspect_element.rb
|
87
86
|
- lib/maruku/maruku.rb
|
@@ -160,6 +159,8 @@ files:
|
|
160
159
|
- spec/block_docs/issue120.md
|
161
160
|
- spec/block_docs/issue123.md
|
162
161
|
- spec/block_docs/issue124.md
|
162
|
+
- spec/block_docs/issue126.md
|
163
|
+
- spec/block_docs/issue130.md
|
163
164
|
- spec/block_docs/issue20.md
|
164
165
|
- spec/block_docs/issue26.md
|
165
166
|
- spec/block_docs/issue29.md
|
@@ -272,7 +273,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
272
273
|
version: '0'
|
273
274
|
requirements: []
|
274
275
|
rubyforge_project:
|
275
|
-
rubygems_version: 2.2.
|
276
|
+
rubygems_version: 2.2.2
|
276
277
|
signing_key:
|
277
278
|
specification_version: 4
|
278
279
|
summary: Maruku is a Markdown-superset interpreter written in Ruby.
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,310 +0,0 @@
|
|
1
|
-
#
|
2
|
-
# NOTA BENE:
|
3
|
-
#
|
4
|
-
# The following algorithm is a rip-off of RubyPants written by
|
5
|
-
# Christian Neukirchen.
|
6
|
-
#
|
7
|
-
# RubyPants is a Ruby port of SmartyPants written by John Gruber.
|
8
|
-
#
|
9
|
-
# This file is distributed under the MIT license, which is compatible
|
10
|
-
# with the terms of the RubyPants license (3-clause BSD).
|
11
|
-
#
|
12
|
-
# -- Andrea Censi
|
13
|
-
|
14
|
-
|
15
|
-
# = RubyPants -- SmartyPants ported to Ruby
|
16
|
-
#
|
17
|
-
# Ported by Christian Neukirchen <mailto:chneukirchen@gmail.com>
|
18
|
-
# Copyright (C) 2004 Christian Neukirchen
|
19
|
-
#
|
20
|
-
# Incooporates ideas, comments and documentation by Chad Miller
|
21
|
-
# Copyright (C) 2004 Chad Miller
|
22
|
-
#
|
23
|
-
# Original SmartyPants by John Gruber
|
24
|
-
# Copyright (C) 2003 John Gruber
|
25
|
-
#
|
26
|
-
|
27
|
-
#
|
28
|
-
# = RubyPants -- SmartyPants ported to Ruby
|
29
|
-
#
|
30
|
-
#
|
31
|
-
# [snip]
|
32
|
-
#
|
33
|
-
# == Authors
|
34
|
-
#
|
35
|
-
# John Gruber did all of the hard work of writing this software in
|
36
|
-
# Perl for Movable Type and almost all of this useful documentation.
|
37
|
-
# Chad Miller ported it to Python to use with Pyblosxom.
|
38
|
-
#
|
39
|
-
# Christian Neukirchen provided the Ruby port, as a general-purpose
|
40
|
-
# library that follows the *Cloth API.
|
41
|
-
#
|
42
|
-
#
|
43
|
-
# == Copyright and License
|
44
|
-
#
|
45
|
-
# === SmartyPants license:
|
46
|
-
#
|
47
|
-
# Copyright (c) 2003 John Gruber
|
48
|
-
# (http://daringfireball.net)
|
49
|
-
# All rights reserved.
|
50
|
-
#
|
51
|
-
# Redistribution and use in source and binary forms, with or without
|
52
|
-
# modification, are permitted provided that the following conditions
|
53
|
-
# are met:
|
54
|
-
#
|
55
|
-
# * Redistributions of source code must retain the above copyright
|
56
|
-
# notice, this list of conditions and the following disclaimer.
|
57
|
-
#
|
58
|
-
# * Redistributions in binary form must reproduce the above copyright
|
59
|
-
# notice, this list of conditions and the following disclaimer in
|
60
|
-
# the documentation and/or other materials provided with the
|
61
|
-
# distribution.
|
62
|
-
#
|
63
|
-
# * Neither the name "SmartyPants" nor the names of its contributors
|
64
|
-
# may be used to endorse or promote products derived from this
|
65
|
-
# software without specific prior written permission.
|
66
|
-
#
|
67
|
-
# This software is provided by the copyright holders and contributors
|
68
|
-
# "as is" and any express or implied warranties, including, but not
|
69
|
-
# limited to, the implied warranties of merchantability and fitness
|
70
|
-
# for a particular purpose are disclaimed. In no event shall the
|
71
|
-
# copyright owner or contributors be liable for any direct, indirect,
|
72
|
-
# incidental, special, exemplary, or consequential damages (including,
|
73
|
-
# but not limited to, procurement of substitute goods or services;
|
74
|
-
# loss of use, data, or profits; or business interruption) however
|
75
|
-
# caused and on any theory of liability, whether in contract, strict
|
76
|
-
# liability, or tort (including negligence or otherwise) arising in
|
77
|
-
# any way out of the use of this software, even if advised of the
|
78
|
-
# possibility of such damage.
|
79
|
-
#
|
80
|
-
# === RubyPants license
|
81
|
-
#
|
82
|
-
# RubyPants is a derivative work of SmartyPants and smartypants.py.
|
83
|
-
#
|
84
|
-
# Redistribution and use in source and binary forms, with or without
|
85
|
-
# modification, are permitted provided that the following conditions
|
86
|
-
# are met:
|
87
|
-
#
|
88
|
-
# * Redistributions of source code must retain the above copyright
|
89
|
-
# notice, this list of conditions and the following disclaimer.
|
90
|
-
#
|
91
|
-
# * Redistributions in binary form must reproduce the above copyright
|
92
|
-
# notice, this list of conditions and the following disclaimer in
|
93
|
-
# the documentation and/or other materials provided with the
|
94
|
-
# distribution.
|
95
|
-
#
|
96
|
-
# This software is provided by the copyright holders and contributors
|
97
|
-
# "as is" and any express or implied warranties, including, but not
|
98
|
-
# limited to, the implied warranties of merchantability and fitness
|
99
|
-
# for a particular purpose are disclaimed. In no event shall the
|
100
|
-
# copyright owner or contributors be liable for any direct, indirect,
|
101
|
-
# incidental, special, exemplary, or consequential damages (including,
|
102
|
-
# but not limited to, procurement of substitute goods or services;
|
103
|
-
# loss of use, data, or profits; or business interruption) however
|
104
|
-
# caused and on any theory of liability, whether in contract, strict
|
105
|
-
# liability, or tort (including negligence or otherwise) arising in
|
106
|
-
# any way out of the use of this software, even if advised of the
|
107
|
-
# possibility of such damage.
|
108
|
-
#
|
109
|
-
#
|
110
|
-
# == Links
|
111
|
-
#
|
112
|
-
# John Gruber:: http://daringfireball.net
|
113
|
-
# SmartyPants:: http://daringfireball.net/projects/smartypants
|
114
|
-
#
|
115
|
-
# Chad Miller:: http://web.chad.org
|
116
|
-
#
|
117
|
-
# Christian Neukirchen:: http://kronavita.de/chris
|
118
|
-
|
119
|
-
|
120
|
-
module MaRuKu::In::Markdown::SpanLevelParser
|
121
|
-
Punct_class = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
|
122
|
-
Close_class = "[^\ \t\r\n\\[\{\(\-]"
|
123
|
-
|
124
|
-
# A rule to apply a particular pattern (like double quotes)
|
125
|
-
# against a list of strings and MDElements, replacing that
|
126
|
-
# pattern with the smartypants version.
|
127
|
-
class Rule
|
128
|
-
# The pattern to search for
|
129
|
-
attr_accessor :pattern
|
130
|
-
|
131
|
-
# The replacement tokens (entities or other instructions)
|
132
|
-
attr_accessor :replacement
|
133
|
-
|
134
|
-
# This is a hack to allow us to build entities.
|
135
|
-
attr_accessor :doc
|
136
|
-
|
137
|
-
def initialize(pattern, replacement)
|
138
|
-
@pattern = pattern
|
139
|
-
@replacement = replacement
|
140
|
-
end
|
141
|
-
|
142
|
-
private
|
143
|
-
|
144
|
-
# Add something to the output array. If it's
|
145
|
-
# not a string (like an MDElement) just add it direcltly,
|
146
|
-
# otherwise attempt to add on to the last element in the
|
147
|
-
# output if it's a string.
|
148
|
-
def append_to_output(output, str)
|
149
|
-
if !str.kind_of?(String)
|
150
|
-
output << str
|
151
|
-
return
|
152
|
-
end
|
153
|
-
return if str.empty?
|
154
|
-
if output.last.kind_of?(String)
|
155
|
-
output.last << str
|
156
|
-
else
|
157
|
-
output << str
|
158
|
-
end
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# Simple rule that says "Replace this pattern with these entities"
|
163
|
-
class ReplaceRule < Rule
|
164
|
-
# Replace all matches in the input at once with the
|
165
|
-
# same elements from "replacement".
|
166
|
-
def apply(first, input, output)
|
167
|
-
split = first.split(pattern)
|
168
|
-
if split.empty?
|
169
|
-
first.scan(pattern).size.times do
|
170
|
-
clone_elems(replacement).each do |x|
|
171
|
-
append_to_output(output, x)
|
172
|
-
end
|
173
|
-
end
|
174
|
-
else
|
175
|
-
intersperse(first.split(pattern), replacement).each do |x|
|
176
|
-
append_to_output(output, x)
|
177
|
-
end
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
private
|
182
|
-
|
183
|
-
# Sort of like "join" - places the elements in "elem"
|
184
|
-
# between each adjacent element in the array.
|
185
|
-
def intersperse(ary, elem)
|
186
|
-
return clone_elems(elem) if ary.empty?
|
187
|
-
return ary if ary.length == 1
|
188
|
-
h, *t = ary
|
189
|
-
t.inject([h]) do |r, e|
|
190
|
-
r.concat clone_elems(elem)
|
191
|
-
r << e
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
|
-
def clone_elems(elems)
|
196
|
-
elems.map do |el|
|
197
|
-
en = el.clone
|
198
|
-
en.doc = doc
|
199
|
-
en
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
# A more complex rule that uses a capture group from the
|
205
|
-
# pattern in its replacement.
|
206
|
-
class CaptureRule < Rule
|
207
|
-
# One at a time, replace each match, including
|
208
|
-
# some part of the match, and put the rest back into
|
209
|
-
# input to be processed next.
|
210
|
-
def apply(first, input, output)
|
211
|
-
if pattern =~ first
|
212
|
-
m = Regexp.last_match
|
213
|
-
append_to_output(output, m.pre_match)
|
214
|
-
input.unshift m.post_match unless m.post_match.empty?
|
215
|
-
replacement.reverse_each do |sub|
|
216
|
-
if sub == :one
|
217
|
-
input.unshift m[1]
|
218
|
-
else
|
219
|
-
entity = sub.clone
|
220
|
-
sub.doc = doc
|
221
|
-
input.unshift entity
|
222
|
-
end
|
223
|
-
end
|
224
|
-
else
|
225
|
-
append_to_output(output, first)
|
226
|
-
end
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
|
-
# All the rules that will be applied (in order) to smarten the document.
|
231
|
-
Rules =
|
232
|
-
[
|
233
|
-
['---', :mdash ],
|
234
|
-
['--', :ndash ],
|
235
|
-
['...', :hellip ],
|
236
|
-
['. . .', :hellip ],
|
237
|
-
["``", :ldquo ],
|
238
|
-
["''", :rdquo ],
|
239
|
-
[/<<\s/, [:laquo, :nbsp] ],
|
240
|
-
[/\s>>/, [:nbsp, :raquo] ],
|
241
|
-
['<<', :laquo ],
|
242
|
-
['>>', :raquo ],
|
243
|
-
|
244
|
-
# Special case if the very first character is a quote followed by
|
245
|
-
# punctuation at a non-word-break. Close the quotes by brute
|
246
|
-
# force:
|
247
|
-
[/\A'(?=#{Punct_class}\B)/, :rsquo],
|
248
|
-
[/\A"(?=#{Punct_class}\B)/, :rdquo],
|
249
|
-
# Special case for double sets of quotes, e.g.:
|
250
|
-
# <p>He said, "'Quoted' words in a larger quote."</p>
|
251
|
-
[/"'(?=\w)/, [:ldquo, :lsquo] ],
|
252
|
-
[/'"(?=\w)/, [:lsquo, :ldquo] ],
|
253
|
-
# Special case for decade abbreviations (the '80s):
|
254
|
-
[/'(?=\d\ds)/, :rsquo ],
|
255
|
-
# Get most opening single quotes:
|
256
|
-
[/(\s)'(?=\w)/, [:one, :lsquo] ],
|
257
|
-
# Single closing quotes:
|
258
|
-
[/(#{Close_class})'/, [:one, :rsquo]],
|
259
|
-
[/'(\s|s\b|$)/, [:rsquo, :one]],
|
260
|
-
# Any remaining single quotes should be opening ones:
|
261
|
-
["'", :lsquo],
|
262
|
-
# Get most opening double quotes:
|
263
|
-
[/(\s)"(?=\w)/, [:one, :ldquo]],
|
264
|
-
# Double closing quotes:
|
265
|
-
[/(#{Close_class})"/, [:one, :rdquo]],
|
266
|
-
[/"(\s|s\b|$)/, [:rdquo, :one]],
|
267
|
-
# Any remaining quotes should be opening ones:
|
268
|
-
['"', :ldquo]
|
269
|
-
].
|
270
|
-
map do |reg, subst| # People should do the thinking, machines should do the work.
|
271
|
-
captures = false
|
272
|
-
subst = Array(subst).map do |s|
|
273
|
-
if s == :one
|
274
|
-
captures = true
|
275
|
-
s
|
276
|
-
else
|
277
|
-
MaRuKu::MDElement.new(:entity, [], { :entity_name => s.to_s.freeze }, nil)
|
278
|
-
end
|
279
|
-
end.freeze
|
280
|
-
|
281
|
-
if captures
|
282
|
-
CaptureRule.new reg, subst
|
283
|
-
else
|
284
|
-
ReplaceRule.new reg, subst
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
# Fully apply a single rule to an entire array
|
289
|
-
# of elements.
|
290
|
-
# note: input will be modified in place
|
291
|
-
def apply_one_rule!(rule, input)
|
292
|
-
output = []
|
293
|
-
while first = input.shift
|
294
|
-
if first.kind_of?(String)
|
295
|
-
rule.doc = @doc
|
296
|
-
rule.apply(first, input, output)
|
297
|
-
else
|
298
|
-
output << first
|
299
|
-
end
|
300
|
-
end
|
301
|
-
output
|
302
|
-
end
|
303
|
-
|
304
|
-
# Transform elements to have SmartyPants punctuation.
|
305
|
-
def educate(elements)
|
306
|
-
Rules.inject(elements) do |elems, rule|
|
307
|
-
apply_one_rule!(rule, elems)
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|