rubypants 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.travis.yml +3 -4
- data/lib/rubypants.rb +99 -72
- data/lib/version.rb +1 -1
- data/test/rubypants_test.rb +26 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 766f0400e12ec2e5bb64917568fcf5dba1b7fdf7e31632c62144602e0b1bd674
|
4
|
+
data.tar.gz: 87de02ec38116b4d9d179e80f958f608101565a9724f63a751e7efb642dab7ff
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 543191639285ce9c53716b19fcbfa72dd6b14e96f53cb88b87a62f5cf4446725ceeb384c6ea3c7767c97409195d7e5cb11c8694d06eb93545284b230b2407bf0
|
7
|
+
data.tar.gz: ed420062908b83fa18c96bb8048870095350f4d9b45ac2294939a9265f775c04a1aa4c5d7e40ed8b3bee741a013103be0bf218e4d18e4e0e59dbbe3eb6cd2fc0
|
data/.travis.yml
CHANGED
data/lib/rubypants.rb
CHANGED
@@ -70,12 +70,23 @@ class RubyPants < String
|
|
70
70
|
|
71
71
|
@options = [*options]
|
72
72
|
@entities = default_entities
|
73
|
-
@entities.merge!(named_entities)
|
73
|
+
@entities.merge!(named_entities) if @options.include?(:named_entities)
|
74
74
|
@entities.merge!(character_entities) if @options.include?(:character_entities)
|
75
|
-
@entities.merge!(character_spaces)
|
75
|
+
@entities.merge!(character_spaces) if @options.include?(:character_spaces)
|
76
76
|
@entities.merge!(entities)
|
77
|
+
|
78
|
+
@single_left_quote = @entities[:single_left_quote]
|
79
|
+
@single_right_quote = @entities[:single_right_quote]
|
80
|
+
@double_left_quote = @entities[:double_left_quote]
|
81
|
+
@double_right_quote = @entities[:double_right_quote]
|
82
|
+
@ellipsis = @entities[:ellipsis]
|
83
|
+
@em_dash = @entities[:em_dash]
|
84
|
+
@en_dash = @entities[:en_dash]
|
77
85
|
end
|
78
86
|
|
87
|
+
SPECIAL_HTML_TAGS = %r!\A<(/?)(pre|code|kbd|script|style|math)[\s>]!
|
88
|
+
NON_WHITESPACE_CHARS = /\S/
|
89
|
+
|
79
90
|
# Apply SmartyPants transformations.
|
80
91
|
def to_html
|
81
92
|
do_quotes = do_backticks = do_dashes = do_ellipses = do_stupify = nil
|
@@ -102,16 +113,16 @@ class RubyPants < String
|
|
102
113
|
end
|
103
114
|
|
104
115
|
# Explicit flags override numeric flag groups.
|
105
|
-
do_quotes = true
|
106
|
-
do_backticks = true
|
107
|
-
do_backticks = :both
|
108
|
-
do_dashes = :normal
|
116
|
+
do_quotes = true if @options.include?(:quotes)
|
117
|
+
do_backticks = true if @options.include?(:backticks)
|
118
|
+
do_backticks = :both if @options.include?(:allbackticks)
|
119
|
+
do_dashes = :normal if @options.include?(:dashes)
|
109
120
|
do_dashes = :oldschool if @options.include?(:oldschool)
|
110
|
-
do_dashes = :inverted
|
111
|
-
prevent_breaks = true
|
112
|
-
do_ellipses = true
|
113
|
-
convert_quotes = true
|
114
|
-
do_stupefy = true
|
121
|
+
do_dashes = :inverted if @options.include?(:inverted)
|
122
|
+
prevent_breaks = true if @options.include?(:prevent_breaks)
|
123
|
+
do_ellipses = true if @options.include?(:ellipses)
|
124
|
+
convert_quotes = true if @options.include?(:convertquotes)
|
125
|
+
do_stupefy = true if @options.include?(:stupefy)
|
115
126
|
|
116
127
|
# Parse the HTML
|
117
128
|
tokens = tokenize
|
@@ -133,7 +144,7 @@ class RubyPants < String
|
|
133
144
|
result << token[1]
|
134
145
|
if token[1].end_with? '/>'
|
135
146
|
# ignore self-closing tags
|
136
|
-
elsif token[1] =~
|
147
|
+
elsif token[1] =~ SPECIAL_HTML_TAGS
|
137
148
|
if $1 == '' && ! in_pre
|
138
149
|
in_pre = $2
|
139
150
|
elsif $1 == '/' && $2 == in_pre
|
@@ -149,36 +160,36 @@ class RubyPants < String
|
|
149
160
|
unless in_pre
|
150
161
|
t = process_escapes t
|
151
162
|
|
152
|
-
t.gsub!(
|
163
|
+
t.gsub!('"', '"') if convert_quotes
|
153
164
|
|
154
165
|
if do_dashes
|
155
|
-
t = educate_dashes t, prevent_breaks
|
156
|
-
t = educate_dashes_oldschool t, prevent_breaks
|
157
|
-
t = educate_dashes_inverted t, prevent_breaks
|
166
|
+
t = educate_dashes t, prevent_breaks if do_dashes == :normal
|
167
|
+
t = educate_dashes_oldschool t, prevent_breaks if do_dashes == :oldschool
|
168
|
+
t = educate_dashes_inverted t, prevent_breaks if do_dashes == :inverted
|
158
169
|
end
|
159
170
|
|
160
|
-
t = educate_ellipses t, prevent_breaks
|
171
|
+
t = educate_ellipses t, prevent_breaks if do_ellipses
|
161
172
|
|
162
173
|
# Note: backticks need to be processed before quotes.
|
163
174
|
if do_backticks
|
164
175
|
t = educate_backticks t
|
165
|
-
t = educate_single_backticks t
|
176
|
+
t = educate_single_backticks t if do_backticks == :both
|
166
177
|
end
|
167
178
|
|
168
179
|
if do_quotes
|
169
180
|
if t == "'"
|
170
181
|
# Special case: single-character ' token
|
171
|
-
if prev_token_last_char =~
|
172
|
-
t =
|
182
|
+
if prev_token_last_char =~ NON_WHITESPACE_CHARS
|
183
|
+
t = @single_right_quote
|
173
184
|
else
|
174
|
-
t =
|
185
|
+
t = @single_left_quote
|
175
186
|
end
|
176
187
|
elsif t == '"'
|
177
188
|
# Special case: single-character " token
|
178
|
-
if prev_token_last_char =~
|
179
|
-
t =
|
189
|
+
if prev_token_last_char =~ NON_WHITESPACE_CHARS
|
190
|
+
t = @double_right_quote
|
180
191
|
else
|
181
|
-
t =
|
192
|
+
t = @double_left_quote
|
182
193
|
end
|
183
194
|
else
|
184
195
|
# Normal case:
|
@@ -186,7 +197,7 @@ class RubyPants < String
|
|
186
197
|
end
|
187
198
|
end
|
188
199
|
|
189
|
-
t = stupefy_entities t
|
200
|
+
t = stupefy_entities t if do_stupefy
|
190
201
|
end
|
191
202
|
|
192
203
|
prev_token_last_char = last_char
|
@@ -227,6 +238,7 @@ class RubyPants < String
|
|
227
238
|
|
228
239
|
DOUBLE_DASH = n_of(2, '-')
|
229
240
|
TRIPLE_DASH = n_of(3, '-')
|
241
|
+
TRIPLE_DOTS = n_of(3, '.')
|
230
242
|
|
231
243
|
# Return +str+ replacing all +patt+ with +repl+. If +prevent_breaks+ is true,
|
232
244
|
# then replace spaces preceding +patt+ with a non-breaking space, and if there
|
@@ -250,7 +262,7 @@ class RubyPants < String
|
|
250
262
|
# em-dash HTML entity.
|
251
263
|
#
|
252
264
|
def educate_dashes(str, prevent_breaks=false)
|
253
|
-
educate(str, DOUBLE_DASH,
|
265
|
+
educate(str, DOUBLE_DASH, @em_dash, prevent_breaks)
|
254
266
|
end
|
255
267
|
|
256
268
|
# Return the string, with each instance of "<tt>--</tt>" translated to an
|
@@ -258,8 +270,8 @@ class RubyPants < String
|
|
258
270
|
# em-dash HTML entity.
|
259
271
|
#
|
260
272
|
def educate_dashes_oldschool(str, prevent_breaks=false)
|
261
|
-
str = educate(str, TRIPLE_DASH,
|
262
|
-
educate(str, DOUBLE_DASH,
|
273
|
+
str = educate(str, TRIPLE_DASH, @em_dash, prevent_breaks)
|
274
|
+
educate(str, DOUBLE_DASH, @en_dash, prevent_breaks)
|
263
275
|
end
|
264
276
|
|
265
277
|
# Return the string, with each instance of "<tt>--</tt>" translated
|
@@ -273,18 +285,20 @@ class RubyPants < String
|
|
273
285
|
# Aaron Swartz for the idea.)
|
274
286
|
#
|
275
287
|
def educate_dashes_inverted(str, prevent_breaks=false)
|
276
|
-
str = educate(str, TRIPLE_DASH,
|
277
|
-
educate(str, DOUBLE_DASH,
|
288
|
+
str = educate(str, TRIPLE_DASH, @en_dash, prevent_breaks)
|
289
|
+
educate(str, DOUBLE_DASH, @em_dash, prevent_breaks)
|
278
290
|
end
|
279
291
|
|
292
|
+
SPACED_ELLIPSIS_PATTERN = /(?<!\.|\.[ ])\.[ ]\.[ ]\.(?!\.|[ ]\.)/
|
293
|
+
|
280
294
|
# Return the string, with each instance of "<tt>...</tt>" translated
|
281
295
|
# to an ellipsis HTML entity. Also converts the case where there are
|
282
296
|
# spaces between the dots.
|
283
297
|
#
|
284
298
|
def educate_ellipses(str, prevent_breaks=false)
|
285
|
-
str = educate(str,
|
286
|
-
educate(str,
|
287
|
-
|
299
|
+
str = educate(str, TRIPLE_DOTS, @ellipsis, prevent_breaks)
|
300
|
+
educate(str, SPACED_ELLIPSIS_PATTERN,
|
301
|
+
@ellipsis, prevent_breaks)
|
288
302
|
end
|
289
303
|
|
290
304
|
# Return the string, with "<tt>``backticks''</tt>"-style single quotes
|
@@ -292,8 +306,8 @@ class RubyPants < String
|
|
292
306
|
#
|
293
307
|
def educate_backticks(str)
|
294
308
|
str.
|
295
|
-
gsub("``",
|
296
|
-
gsub("''",
|
309
|
+
gsub("``", @double_left_quote).
|
310
|
+
gsub("''", @double_right_quote)
|
297
311
|
end
|
298
312
|
|
299
313
|
# Return the string, with "<tt>`backticks'</tt>"-style single quotes
|
@@ -301,66 +315,79 @@ class RubyPants < String
|
|
301
315
|
#
|
302
316
|
def educate_single_backticks(str)
|
303
317
|
str.
|
304
|
-
gsub("`",
|
305
|
-
gsub("'",
|
318
|
+
gsub("`", @single_left_quote).
|
319
|
+
gsub("'", @single_right_quote)
|
306
320
|
end
|
307
321
|
|
322
|
+
PUNCT_CLASS = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'.freeze
|
323
|
+
SNGL_QUOT_PUNCT_CASE = /^'(?=#{PUNCT_CLASS}\B)/
|
324
|
+
DBLE_QUOT_PUNCT_CASE = /^"(?=#{PUNCT_CLASS}\B)/
|
325
|
+
|
326
|
+
STARTS_MIXED_QUOTS_WITH_SNGL = /'"(?=\w)/
|
327
|
+
STARTS_MIXED_QUOTS_WITH_DBLE = /"'(?=\w)/
|
328
|
+
|
329
|
+
DECADE_ABBR_CASE = /'(?=\d\ds)/
|
330
|
+
|
331
|
+
CLOSE_CLASS = '[^\ \t\r\n\\[\{\(\-]'.freeze
|
332
|
+
CHAR_LEADS_SNGL_QUOTE = /(#{CLOSE_CLASS})'/
|
333
|
+
WHITESPACE_TRAILS_SNGL_QUOTE = /'(\s|s\b|$)/
|
334
|
+
CHAR_LEADS_DBLE_QUOTE = /(#{CLOSE_CLASS})"/
|
335
|
+
WHITESPACE_TRAILS_DBLE_QUOTE = /"(\s|s\b|$)/
|
336
|
+
|
308
337
|
# Return the string, with "educated" curly quote HTML entities.
|
309
338
|
#
|
310
339
|
def educate_quotes(str)
|
311
|
-
punct_class = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'
|
312
|
-
|
313
340
|
str = str.dup
|
314
341
|
|
315
342
|
# Special case if the very first character is a quote followed by
|
316
343
|
# punctuation at a non-word-break. Close the quotes by brute
|
317
344
|
# force:
|
318
|
-
str.gsub!(
|
319
|
-
|
320
|
-
str.gsub!(
|
321
|
-
|
345
|
+
str.gsub!(SNGL_QUOT_PUNCT_CASE,
|
346
|
+
@single_right_quote)
|
347
|
+
str.gsub!(DBLE_QUOT_PUNCT_CASE,
|
348
|
+
@double_right_quote)
|
322
349
|
|
323
350
|
# Special case for double sets of quotes, e.g.:
|
324
351
|
# <p>He said, "'Quoted' words in a larger quote."</p>
|
325
|
-
str.gsub!(
|
326
|
-
"#{
|
327
|
-
str.gsub!(
|
328
|
-
"#{
|
352
|
+
str.gsub!(STARTS_MIXED_QUOTS_WITH_DBLE,
|
353
|
+
"#{@double_left_quote}#{@single_left_quote}")
|
354
|
+
str.gsub!(STARTS_MIXED_QUOTS_WITH_SNGL,
|
355
|
+
"#{@single_left_quote}#{@double_left_quote}")
|
329
356
|
|
330
357
|
# Special case for decade abbreviations (the '80s):
|
331
|
-
str.gsub!(
|
332
|
-
|
358
|
+
str.gsub!(DECADE_ABBR_CASE,
|
359
|
+
@single_right_quote)
|
333
360
|
|
334
|
-
|
335
|
-
|
361
|
+
dec_dashes = "#{@en_dash}|#{@em_dash}"
|
362
|
+
quote_precedent = "[[:space:]]| |--|&[mn]dash;|#{dec_dashes}|ȁ[34];"
|
336
363
|
|
337
364
|
# Get most opening single quotes:
|
338
|
-
str.gsub!(/(
|
339
|
-
'\1' +
|
365
|
+
str.gsub!(/(#{quote_precedent})'(?=\w)/,
|
366
|
+
'\1' + @single_left_quote)
|
340
367
|
|
341
368
|
# Single closing quotes:
|
342
|
-
str.gsub!(
|
343
|
-
'\1' +
|
344
|
-
str.gsub!(
|
345
|
-
|
369
|
+
str.gsub!(CHAR_LEADS_SNGL_QUOTE,
|
370
|
+
'\1' + @single_right_quote)
|
371
|
+
str.gsub!(WHITESPACE_TRAILS_SNGL_QUOTE,
|
372
|
+
@single_right_quote + '\1')
|
346
373
|
|
347
374
|
# Any remaining single quotes should be opening ones:
|
348
|
-
str.gsub!(
|
349
|
-
|
375
|
+
str.gsub!("'",
|
376
|
+
@single_left_quote)
|
350
377
|
|
351
378
|
# Get most opening double quotes:
|
352
|
-
str.gsub!(/(
|
353
|
-
'\1' +
|
379
|
+
str.gsub!(/(#{quote_precedent})"(?=\w)/,
|
380
|
+
'\1' + @double_left_quote)
|
354
381
|
|
355
382
|
# Double closing quotes:
|
356
|
-
str.gsub!(
|
357
|
-
'\1' +
|
358
|
-
str.gsub!(
|
359
|
-
|
383
|
+
str.gsub!(CHAR_LEADS_DBLE_QUOTE,
|
384
|
+
'\1' + @double_right_quote)
|
385
|
+
str.gsub!(WHITESPACE_TRAILS_DBLE_QUOTE,
|
386
|
+
@double_right_quote + '\1')
|
360
387
|
|
361
388
|
# Any remaining quotes should be opening ones:
|
362
|
-
str.gsub!(
|
363
|
-
|
389
|
+
str.gsub!('"',
|
390
|
+
@double_left_quote)
|
364
391
|
|
365
392
|
str
|
366
393
|
end
|
@@ -382,12 +409,14 @@ class RubyPants < String
|
|
382
409
|
:double_right_quote => '"',
|
383
410
|
:ellipsis => '...'
|
384
411
|
}.each do |k,v|
|
385
|
-
new_str.gsub!(
|
412
|
+
new_str.gsub!(entity(k).to_s, v)
|
386
413
|
end
|
387
414
|
|
388
415
|
new_str
|
389
416
|
end
|
390
417
|
|
418
|
+
TAG_SOUP = /([^<]*)(<!--.*?-->|<[^>]*>)/m
|
419
|
+
|
391
420
|
# Return an array of the tokens comprising the string. Each token is
|
392
421
|
# either a tag (possibly with nested, tags contained therein, such
|
393
422
|
# as <tt><a href="<MTFoo>"></tt>, or a run of text between
|
@@ -401,14 +430,12 @@ class RubyPants < String
|
|
401
430
|
# Chad Miller in the Python port of SmartyPants.
|
402
431
|
#
|
403
432
|
def tokenize
|
404
|
-
tag_soup = /([^<]*)(<!--.*?-->|<[^>]*>)/m
|
405
|
-
|
406
433
|
tokens = []
|
407
434
|
|
408
435
|
prev_end = 0
|
409
436
|
|
410
|
-
scan(
|
411
|
-
tokens << [:text, $1]
|
437
|
+
scan(TAG_SOUP) do
|
438
|
+
tokens << [:text, $1] if $1 != ""
|
412
439
|
tokens << [:tag, $2]
|
413
440
|
prev_end = $~.end(0)
|
414
441
|
end
|
data/lib/version.rb
CHANGED
data/test/rubypants_test.rb
CHANGED
@@ -11,6 +11,10 @@ class RubyPantsTest < Minitest::Test
|
|
11
11
|
assert_equal orig, RubyPants.new(str, options, entities).to_html
|
12
12
|
end
|
13
13
|
|
14
|
+
def refute_rp_equal(str, orig, options=[2], entities = {})
|
15
|
+
refute_equal orig, RubyPants.new(str, options, entities).to_html
|
16
|
+
end
|
17
|
+
|
14
18
|
def assert_verbatim(str)
|
15
19
|
assert_rp_equal str, str
|
16
20
|
end
|
@@ -152,6 +156,28 @@ EOF
|
|
152
156
|
assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses, :prevent_breaks]
|
153
157
|
assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses, :prevent_breaks]
|
154
158
|
|
159
|
+
# dots and tab-spaces
|
160
|
+
refute_rp_equal "foo. . .bar", 'foo…bar', [:ellipses]
|
161
|
+
refute_rp_equal "foo . . . bar", 'foo … bar', [:ellipses]
|
162
|
+
assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses]
|
163
|
+
assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses]
|
164
|
+
# and with :prevent_breaks
|
165
|
+
refute_rp_equal "foo. . .bar", 'foo⁠…bar', [:ellipses, :prevent_breaks]
|
166
|
+
refute_rp_equal "foo . . . bar", 'foo … bar', [:ellipses, :prevent_breaks]
|
167
|
+
assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses, :prevent_breaks]
|
168
|
+
assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses, :prevent_breaks]
|
169
|
+
|
170
|
+
# dots and line-breaks
|
171
|
+
refute_rp_equal "foo.\n.\n.bar", 'foo…bar', [:ellipses]
|
172
|
+
refute_rp_equal "foo\n.\n.\n.\nbar", "foo\n…\nbar", [:ellipses]
|
173
|
+
assert_rp_equal "foo.\n.\n.\n.bar", "foo.\n.\n.\n.bar", [:ellipses]
|
174
|
+
assert_rp_equal "foo\n.\n.\n.\n.\nbar", "foo\n.\n.\n.\n.\nbar", [:ellipses]
|
175
|
+
# and with :prevent_breaks
|
176
|
+
refute_rp_equal "foo.\n.\n.bar", "foo⁠…bar", [:ellipses, :prevent_breaks]
|
177
|
+
refute_rp_equal "foo\n.\n.\n.\nbar", "foo …\nbar", [:ellipses, :prevent_breaks]
|
178
|
+
assert_rp_equal "foo.\n.\n.\n.bar", "foo.\n.\n.\n.bar", [:ellipses, :prevent_breaks]
|
179
|
+
assert_rp_equal "foo\n.\n.\n.\n.\nbar", "foo\n.\n.\n.\n.\nbar", [:ellipses, :prevent_breaks]
|
180
|
+
|
155
181
|
# nasty ones
|
156
182
|
assert_rp_equal "foo. . ..bar", 'foo. . ..bar', [:ellipses]
|
157
183
|
assert_rp_equal "foo. . ...bar", 'foo. . …bar', [:ellipses]
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubypants
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.7.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Gruber
|
@@ -12,7 +12,7 @@ authors:
|
|
12
12
|
autorequire:
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
|
-
date:
|
15
|
+
date: 2019-12-30 00:00:00.000000000 Z
|
16
16
|
dependencies:
|
17
17
|
- !ruby/object:Gem::Dependency
|
18
18
|
name: minitest
|
@@ -68,8 +68,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
68
|
- !ruby/object:Gem::Version
|
69
69
|
version: '0'
|
70
70
|
requirements: []
|
71
|
-
|
72
|
-
rubygems_version: 2.6.13
|
71
|
+
rubygems_version: 3.0.3
|
73
72
|
signing_key:
|
74
73
|
specification_version: 4
|
75
74
|
summary: RubyPants is a Ruby port of the smart-quotes library SmartyPants.
|