maruku 0.7.1 → 0.7.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
["\\![a](url)", ['!', 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
|