github-markdown 0.5.2 → 0.6.9
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 +7 -0
- data/Rakefile +30 -2
- data/ext/markdown/autolink.c +39 -29
- data/ext/markdown/markdown.c +143 -56
- data/ext/markdown/plaintext.c +1 -7
- data/github-markdown.gemspec +4 -7
- data/test/gfm_test.rb +28 -1
- metadata +28 -61
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a2f2a20e79b2838e629c702b785c60edc0fd286f
|
4
|
+
data.tar.gz: 633a0cb21dfb034293b8449b7f65bcc77d51e96b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 2d0fa591d20cfcaa459fde53061ad9c59da05f2f6a6163dc79305d29b971663c16ccedf3882a01039e1ffbfd29e86cad2e6d15f4459b40a2568b51fe7693d2e6
|
7
|
+
data.tar.gz: 5c705bf59ec494a7032c2e94821a7d12713073381e8c2658c2c5d8be26e6151473389aa8973e1716041b4b5c583677733cb77e157a037451e88c7384ab5b9fc4
|
data/Rakefile
CHANGED
@@ -14,6 +14,22 @@ Rake::ExtensionTask.new('markdown') do |ext|
|
|
14
14
|
ext.lib_dir = 'lib/github'
|
15
15
|
end
|
16
16
|
|
17
|
+
# ==========================================================
|
18
|
+
# Helpers
|
19
|
+
# ==========================================================
|
20
|
+
|
21
|
+
def name
|
22
|
+
@name ||= "github-markdown"
|
23
|
+
end
|
24
|
+
|
25
|
+
def version
|
26
|
+
@version ||= File.read("#{name}.gemspec").match(/^\s*s.version\s*=\s*['"](.*)['"]/)[1]
|
27
|
+
end
|
28
|
+
|
29
|
+
def gem_file
|
30
|
+
"#{name}-#{version}.gem"
|
31
|
+
end
|
32
|
+
|
17
33
|
# ==========================================================
|
18
34
|
# Testing
|
19
35
|
# ==========================================================
|
@@ -44,6 +60,20 @@ file package('.gem') => %w[pkg/ github-markdown.gemspec] + $spec.files do |f|
|
|
44
60
|
mv File.basename(f.name), f.name
|
45
61
|
end
|
46
62
|
|
63
|
+
|
64
|
+
desc "Create tag v#{version} and build and push #{gem_file} to Rubygems"
|
65
|
+
task :release => :package do
|
66
|
+
unless `git branch` =~ /^\* master$/
|
67
|
+
puts "You must be on the master branch to release!"
|
68
|
+
exit!
|
69
|
+
end
|
70
|
+
sh "git commit --allow-empty -a -m 'Release #{version}'"
|
71
|
+
sh "git tag v#{version}"
|
72
|
+
sh "git push origin master"
|
73
|
+
sh "git push origin v#{version}"
|
74
|
+
sh "gem push pkg/#{name}-#{version}.gem"
|
75
|
+
end
|
76
|
+
|
47
77
|
# GEMSPEC HELPERS ==========================================================
|
48
78
|
|
49
79
|
task :update_gem do
|
@@ -84,5 +114,3 @@ end
|
|
84
114
|
file 'sundown/src/markdown.h' do |t|
|
85
115
|
abort "The Sundown submodule is required."
|
86
116
|
end
|
87
|
-
|
88
|
-
|
data/ext/markdown/autolink.c
CHANGED
@@ -51,7 +51,7 @@ sd_autolink_issafe(const uint8_t *link, size_t link_len)
|
|
51
51
|
static size_t
|
52
52
|
autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
53
53
|
{
|
54
|
-
uint8_t cclose, copen
|
54
|
+
uint8_t cclose, copen;
|
55
55
|
size_t i;
|
56
56
|
|
57
57
|
for (i = 0; i < link_end; ++i)
|
@@ -61,7 +61,7 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
|
61
61
|
}
|
62
62
|
|
63
63
|
while (link_end > 0) {
|
64
|
-
if (strchr("
|
64
|
+
if (strchr("?!.,:", data[link_end - 1]) != NULL)
|
65
65
|
link_end--;
|
66
66
|
|
67
67
|
else if (data[link_end - 1] == ';') {
|
@@ -78,20 +78,21 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
|
78
78
|
else break;
|
79
79
|
}
|
80
80
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
}
|
81
|
+
while (link_end > 0) {
|
82
|
+
cclose = data[link_end - 1];
|
83
|
+
|
84
|
+
switch (cclose) {
|
85
|
+
case '"': copen = '"'; break;
|
86
|
+
case '\'': copen = '\''; break;
|
87
|
+
case ')': copen = '('; break;
|
88
|
+
case ']': copen = '['; break;
|
89
|
+
case '}': copen = '{'; break;
|
90
|
+
default: copen = 0;
|
91
|
+
}
|
93
92
|
|
94
|
-
|
93
|
+
if (copen == 0)
|
94
|
+
break;
|
95
|
+
|
95
96
|
size_t closing = 0;
|
96
97
|
size_t opening = 0;
|
97
98
|
size_t i = 0;
|
@@ -110,7 +111,7 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
|
110
111
|
* => http://www.pokemon.com/Pikachu_(Electric)
|
111
112
|
*
|
112
113
|
* foo http://www.pokemon.com/Pikachu_(Electric)) bar
|
113
|
-
* => http://www.pokemon.com/Pikachu_(Electric)
|
114
|
+
* => http://www.pokemon.com/Pikachu_(Electric)
|
114
115
|
*
|
115
116
|
* (foo http://www.pokemon.com/Pikachu_(Electric)) bar
|
116
117
|
* => foo http://www.pokemon.com/Pikachu_(Electric)
|
@@ -125,8 +126,10 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
|
125
126
|
i++;
|
126
127
|
}
|
127
128
|
|
128
|
-
if (closing
|
129
|
-
|
129
|
+
if (closing == opening)
|
130
|
+
break;
|
131
|
+
|
132
|
+
link_end--;
|
130
133
|
}
|
131
134
|
|
132
135
|
return link_end;
|
@@ -135,16 +138,20 @@ autolink_delim(uint8_t *data, size_t link_end, size_t max_rewind, size_t size)
|
|
135
138
|
static size_t
|
136
139
|
check_domain(uint8_t *data, size_t size, int allow_short)
|
137
140
|
{
|
138
|
-
size_t i, np = 0;
|
141
|
+
size_t i, np = 0, uscore1 = 0, uscore2 = 0;
|
139
142
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
143
|
+
for (i = 1; i < size - 1; i++) {
|
144
|
+
if (data[i] == '_') uscore2++;
|
145
|
+
else if (data[i] == '.') {
|
146
|
+
uscore1 = uscore2;
|
147
|
+
uscore2 = 0;
|
148
|
+
np++;
|
149
|
+
}
|
145
150
|
else if (!isalnum(data[i]) && data[i] != '-') break;
|
146
151
|
}
|
147
152
|
|
153
|
+
if (uscore1 > 0 || uscore2 > 0) return 0;
|
154
|
+
|
148
155
|
if (allow_short) {
|
149
156
|
/* We don't need a valid domain in the strict sense (with
|
150
157
|
* least one dot; so just make sure it's composed of valid
|
@@ -169,7 +176,7 @@ sd_autolink__www(
|
|
169
176
|
{
|
170
177
|
size_t link_end;
|
171
178
|
|
172
|
-
if (max_rewind > 0 &&
|
179
|
+
if (max_rewind > 0 && data[-1] != '(' && data[-1] != '[' && !isspace(data[-1]))
|
173
180
|
return 0;
|
174
181
|
|
175
182
|
if (size < 4 || memcmp(data, "www.", strlen("www.")) != 0)
|
@@ -204,7 +211,7 @@ sd_autolink__email(
|
|
204
211
|
unsigned int flags)
|
205
212
|
{
|
206
213
|
size_t link_end, rewind;
|
207
|
-
int nb = 0, np = 0;
|
214
|
+
int nb = 0, np = 0, ns = 0;
|
208
215
|
|
209
216
|
for (rewind = 0; rewind < max_rewind; ++rewind) {
|
210
217
|
uint8_t c = data[-rewind - 1];
|
@@ -215,10 +222,13 @@ sd_autolink__email(
|
|
215
222
|
if (strchr(".+-_", c) != NULL)
|
216
223
|
continue;
|
217
224
|
|
225
|
+
if (c == '/')
|
226
|
+
ns++;
|
227
|
+
|
218
228
|
break;
|
219
229
|
}
|
220
230
|
|
221
|
-
if (rewind == 0)
|
231
|
+
if (rewind == 0 || ns > 0)
|
222
232
|
return 0;
|
223
233
|
|
224
234
|
for (link_end = 0; link_end < size; ++link_end) {
|
@@ -235,7 +245,8 @@ sd_autolink__email(
|
|
235
245
|
break;
|
236
246
|
}
|
237
247
|
|
238
|
-
if (link_end < 2 || nb != 1 || np == 0
|
248
|
+
if (link_end < 2 || nb != 1 || np == 0 ||
|
249
|
+
(!isalpha(data[link_end - 1]) && data[link_end - 1] != '.'))
|
239
250
|
return 0;
|
240
251
|
|
241
252
|
link_end = autolink_delim(data, link_end, max_rewind, size);
|
@@ -293,4 +304,3 @@ sd_autolink__url(
|
|
293
304
|
|
294
305
|
return link_end;
|
295
306
|
}
|
296
|
-
|
data/ext/markdown/markdown.c
CHANGED
@@ -233,6 +233,15 @@ free_link_refs(struct link_ref **references)
|
|
233
233
|
}
|
234
234
|
}
|
235
235
|
|
236
|
+
/*
|
237
|
+
Wrap isalnum so that characters outside of the ASCII range don't count.
|
238
|
+
*/
|
239
|
+
static inline int
|
240
|
+
_isalnum(int c)
|
241
|
+
{
|
242
|
+
return isalnum(c) && c < 0x7f;
|
243
|
+
}
|
244
|
+
|
236
245
|
/*
|
237
246
|
* Check whether a char is a Markdown space.
|
238
247
|
|
@@ -297,25 +306,24 @@ tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
|
|
297
306
|
|
298
307
|
/* begins with a '<' optionally followed by '/', followed by letter or number */
|
299
308
|
if (data[0] != '<') return 0;
|
309
|
+
|
310
|
+
if ((i = is_mail_autolink(data + 1, size - 1)) != 0) {
|
311
|
+
*autolink = MKDA_EMAIL;
|
312
|
+
return i + 1;
|
313
|
+
}
|
314
|
+
|
300
315
|
i = (data[1] == '/') ? 2 : 1;
|
301
316
|
|
302
|
-
if (!
|
317
|
+
if (!_isalnum(data[i]))
|
303
318
|
return 0;
|
304
319
|
|
305
320
|
/* scheme test */
|
306
321
|
*autolink = MKDA_NOT_AUTOLINK;
|
307
322
|
|
308
323
|
/* try to find the beginning of an URI */
|
309
|
-
while (i < size && (
|
324
|
+
while (i < size && (_isalnum(data[i]) || data[i] == '.' || data[i] == '+' || data[i] == '-'))
|
310
325
|
i++;
|
311
326
|
|
312
|
-
if (i > 1 && data[i] == '@') {
|
313
|
-
if ((j = is_mail_autolink(data + i, size - i)) != 0) {
|
314
|
-
*autolink = MKDA_EMAIL;
|
315
|
-
return i + j;
|
316
|
-
}
|
317
|
-
}
|
318
|
-
|
319
327
|
if (i > 2 && data[i] == ':') {
|
320
328
|
*autolink = MKDA_NORMAL;
|
321
329
|
i++;
|
@@ -352,7 +360,7 @@ tag_length(uint8_t *data, size_t size, enum mkd_autolink *autolink)
|
|
352
360
|
static void
|
353
361
|
parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size)
|
354
362
|
{
|
355
|
-
size_t i = 0, end = 0;
|
363
|
+
size_t i = 0, end = 0, consumed = 0;
|
356
364
|
uint8_t action = 0;
|
357
365
|
struct buf work = { 0, 0, 0, 0 };
|
358
366
|
|
@@ -377,12 +385,13 @@ parse_inline(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t siz
|
|
377
385
|
if (end >= size) break;
|
378
386
|
i = end;
|
379
387
|
|
380
|
-
end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i, size - i);
|
388
|
+
end = markdown_char_ptrs[(int)action](ob, rndr, data + i, i - consumed, size - i);
|
381
389
|
if (!end) /* no action from the callback */
|
382
390
|
end = i + 1;
|
383
391
|
else {
|
384
392
|
i += end;
|
385
393
|
end = i;
|
394
|
+
consumed = i;
|
386
395
|
}
|
387
396
|
}
|
388
397
|
}
|
@@ -394,7 +403,7 @@ find_emph_char(uint8_t *data, size_t size, uint8_t c)
|
|
394
403
|
size_t i = 1;
|
395
404
|
|
396
405
|
while (i < size) {
|
397
|
-
while (i < size && data[i] != c && data[i] != '
|
406
|
+
while (i < size && data[i] != c && data[i] != '[')
|
398
407
|
i++;
|
399
408
|
|
400
409
|
if (i == size)
|
@@ -500,8 +509,8 @@ parse_emph1(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size
|
|
500
509
|
|
501
510
|
if (data[i] == c && !_isspace(data[i - 1])) {
|
502
511
|
|
503
|
-
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
|
504
|
-
if (i + 1 < size &&
|
512
|
+
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS && c == '_') {
|
513
|
+
if (i + 1 < size && (_isalnum(data[i + 1]) || data[i + 1] == c))
|
505
514
|
continue;
|
506
515
|
}
|
507
516
|
|
@@ -536,6 +545,12 @@ parse_emph2(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size
|
|
536
545
|
i += len;
|
537
546
|
|
538
547
|
if (i + 1 < size && data[i] == c && data[i + 1] == c && i && !_isspace(data[i - 1])) {
|
548
|
+
|
549
|
+
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS && c == '_') {
|
550
|
+
if (i + 2 < size && (_isalnum(data[i + 2]) || data[i + 2] == c))
|
551
|
+
continue;
|
552
|
+
}
|
553
|
+
|
539
554
|
work = rndr_newbuf(rndr, BUFFER_SPAN);
|
540
555
|
parse_inline(work, rndr, data, i);
|
541
556
|
r = render_method(ob, work, rndr->opaque);
|
@@ -565,6 +580,12 @@ parse_emph3(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size
|
|
565
580
|
continue;
|
566
581
|
|
567
582
|
if (i + 2 < size && data[i + 1] == c && data[i + 2] == c && rndr->cb.triple_emphasis) {
|
583
|
+
|
584
|
+
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS && c == '_') {
|
585
|
+
if (i + 3 < size && (_isalnum(data[i + 3]) || data[i + 3] == c))
|
586
|
+
continue;
|
587
|
+
}
|
588
|
+
|
568
589
|
/* triple symbol found */
|
569
590
|
struct buf *work = rndr_newbuf(rndr, BUFFER_SPAN);
|
570
591
|
|
@@ -596,9 +617,10 @@ char_emphasis(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t of
|
|
596
617
|
uint8_t c = data[0];
|
597
618
|
size_t ret;
|
598
619
|
|
599
|
-
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS) {
|
600
|
-
if (offset > 0 && !_isspace(data[-1]) && data[-1]
|
620
|
+
if (rndr->ext_flags & MKDEXT_NO_INTRA_EMPHASIS && c == '_') {
|
621
|
+
if (offset > 0 && !_isspace(data[-1]) && (_isalnum(data[-1]) || data[-1] == data[0])) {
|
601
622
|
return 0;
|
623
|
+
}
|
602
624
|
}
|
603
625
|
|
604
626
|
if (size > 2 && data[1] != c) {
|
@@ -721,7 +743,7 @@ char_entity(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offs
|
|
721
743
|
if (end < size && data[end] == '#')
|
722
744
|
end++;
|
723
745
|
|
724
|
-
while (end < size &&
|
746
|
+
while (end < size && _isalnum(data[end]))
|
725
747
|
end++;
|
726
748
|
|
727
749
|
if (end < size && data[end] == ';')
|
@@ -895,10 +917,18 @@ char_link(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t offset
|
|
895
917
|
link_b = i;
|
896
918
|
|
897
919
|
/* looking for link end: ' " ) */
|
920
|
+
/* Count the number of open parenthesis */
|
921
|
+
size_t nb_p = 0;
|
922
|
+
|
898
923
|
while (i < size) {
|
899
924
|
if (data[i] == '\\') i += 2;
|
900
|
-
else if (data[i] == '
|
901
|
-
|
925
|
+
else if (data[i] == '(' && i != 0) {
|
926
|
+
nb_p++; i++;
|
927
|
+
}
|
928
|
+
else if (data[i] == ')') {
|
929
|
+
if (nb_p == 0) break;
|
930
|
+
nb_p--; i++;
|
931
|
+
} else if (i >= 1 && _isspace(data[i-1]) && (data[i] == '\'' || data[i] == '"')) break;
|
902
932
|
else i++;
|
903
933
|
}
|
904
934
|
|
@@ -1364,7 +1394,7 @@ prefix_uli(uint8_t *data, size_t size)
|
|
1364
1394
|
|
1365
1395
|
if (i < size && data[i] == ' ') i++;
|
1366
1396
|
if (i < size && data[i] == ' ') i++;
|
1367
|
-
|
1397
|
+
if (i < size && data[i] == ' ') i++;
|
1368
1398
|
|
1369
1399
|
if (i + 1 >= size ||
|
1370
1400
|
(data[i] != '*' && data[i] != '+' && data[i] != '-') ||
|
@@ -1461,7 +1491,7 @@ parse_paragraph(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t
|
|
1461
1491
|
* let's check to see if there's some kind of block starting
|
1462
1492
|
* here
|
1463
1493
|
*/
|
1464
|
-
if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !
|
1494
|
+
if ((rndr->ext_flags & MKDEXT_LAX_SPACING) && !_isalnum(data[i])) {
|
1465
1495
|
if (prefix_oli(data + i, size - i) ||
|
1466
1496
|
prefix_uli(data + i, size - i)) {
|
1467
1497
|
end = i;
|
@@ -1629,8 +1659,8 @@ static size_t
|
|
1629
1659
|
parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t size, int *flags)
|
1630
1660
|
{
|
1631
1661
|
struct buf *work = 0, *inter = 0;
|
1632
|
-
size_t beg = 0, end, pre, sublist = 0, orgpre = 0, i;
|
1633
|
-
int
|
1662
|
+
size_t beg = 0, end, pre, sublist = 0, orgpre = 0, previous_indent = 0, i;
|
1663
|
+
int empty_lines = 0, has_inside_empty = 0, has_trailing_empty = 0, in_fence = 0, previous_indent_diff = 0;
|
1634
1664
|
|
1635
1665
|
/* keeping track of the first indentation prefix */
|
1636
1666
|
while (orgpre < 3 && orgpre < size && data[orgpre] == ' ')
|
@@ -1667,7 +1697,7 @@ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t s
|
|
1667
1697
|
|
1668
1698
|
/* process an empty line */
|
1669
1699
|
if (is_empty(data + beg, end - beg)) {
|
1670
|
-
|
1700
|
+
empty_lines++;
|
1671
1701
|
beg = end;
|
1672
1702
|
continue;
|
1673
1703
|
}
|
@@ -1677,11 +1707,26 @@ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t s
|
|
1677
1707
|
while (i < 4 && beg + i < end && data[beg + i] == ' ')
|
1678
1708
|
i++;
|
1679
1709
|
|
1710
|
+
/* don't bother calculating, this is probably the first item if == 0 */
|
1711
|
+
if (previous_indent > 0) {
|
1712
|
+
previous_indent_diff = i - previous_indent;
|
1713
|
+
/* allow indentations between 2 and 4 spaces to count as a new list */
|
1714
|
+
if (previous_indent_diff > 1 && previous_indent_diff < 4)
|
1715
|
+
i = 2;
|
1716
|
+
}
|
1717
|
+
|
1680
1718
|
pre = i;
|
1719
|
+
previous_indent = pre;
|
1681
1720
|
|
1682
1721
|
if (rndr->ext_flags & MKDEXT_FENCED_CODE) {
|
1683
|
-
if (is_codefence(data + beg + i, end - beg - i, NULL) != 0)
|
1722
|
+
if (is_codefence(data + beg + i, end - beg - i, NULL) != 0) {
|
1723
|
+
/* If the fenced code isn't indented, then end the list */
|
1724
|
+
if (pre == 0 && !in_fence) {
|
1725
|
+
*flags |= MKD_LI_END;
|
1726
|
+
break;
|
1727
|
+
}
|
1684
1728
|
in_fence = !in_fence;
|
1729
|
+
}
|
1685
1730
|
}
|
1686
1731
|
|
1687
1732
|
/* Only check for new list items if we are **not** inside
|
@@ -1691,46 +1736,48 @@ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t s
|
|
1691
1736
|
has_next_oli = prefix_oli(data + beg + i, end - beg - i);
|
1692
1737
|
}
|
1693
1738
|
|
1694
|
-
/* checking for ul/ol switch */
|
1695
|
-
if (in_empty && (
|
1696
|
-
((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
|
1697
|
-
(!(*flags & MKD_LIST_ORDERED) && has_next_oli))){
|
1698
|
-
*flags |= MKD_LI_END;
|
1699
|
-
break; /* the following item must have same list type */
|
1700
|
-
}
|
1701
|
-
|
1702
1739
|
/* checking for a new item */
|
1703
1740
|
if ((has_next_uli && !is_hrule(data + beg + i, end - beg - i)) || has_next_oli) {
|
1704
|
-
|
1705
|
-
|
1706
|
-
|
1707
|
-
|
1708
|
-
|
1741
|
+
/* the following item must have the same indentation */
|
1742
|
+
if (pre == orgpre) {
|
1743
|
+
if (empty_lines > 0) {
|
1744
|
+
has_trailing_empty = 1;
|
1745
|
+
|
1746
|
+
/* checking for ul/ol switch */
|
1747
|
+
if (((*flags & MKD_LIST_ORDERED) && has_next_uli) ||
|
1748
|
+
(!(*flags & MKD_LIST_ORDERED) && has_next_oli)) {
|
1749
|
+
*flags |= MKD_LI_END;
|
1750
|
+
}
|
1751
|
+
}
|
1752
|
+
break;
|
1753
|
+
}
|
1709
1754
|
|
1710
1755
|
if (!sublist)
|
1711
1756
|
sublist = work->size;
|
1712
1757
|
}
|
1758
|
+
|
1713
1759
|
/* joining only indented stuff after empty lines;
|
1714
1760
|
* note that now we only require 1 space of indentation
|
1715
1761
|
* to continue a list */
|
1716
|
-
|
1762
|
+
if (empty_lines > 0 && pre == 0 && !in_fence) {
|
1717
1763
|
*flags |= MKD_LI_END;
|
1718
1764
|
break;
|
1719
1765
|
}
|
1720
|
-
else if (
|
1721
|
-
|
1766
|
+
else if (empty_lines > 0) {
|
1767
|
+
/* preserve all the empty lines because they
|
1768
|
+
* may be meaningful inside a code block */
|
1769
|
+
for (; empty_lines > 0; empty_lines--)
|
1770
|
+
bufputc(work, '\n');
|
1722
1771
|
has_inside_empty = 1;
|
1723
1772
|
}
|
1724
1773
|
|
1725
|
-
in_empty = 0;
|
1726
|
-
|
1727
1774
|
/* adding the line without prefix into the working buffer */
|
1728
1775
|
bufput(work, data + beg + i, end - beg - i);
|
1729
1776
|
beg = end;
|
1730
1777
|
}
|
1731
1778
|
|
1732
1779
|
/* render of li contents */
|
1733
|
-
if (has_inside_empty)
|
1780
|
+
if (has_inside_empty || has_trailing_empty)
|
1734
1781
|
*flags |= MKD_LI_BLOCK;
|
1735
1782
|
|
1736
1783
|
if (*flags & MKD_LI_BLOCK) {
|
@@ -1751,6 +1798,9 @@ parse_listitem(struct buf *ob, struct sd_markdown *rndr, uint8_t *data, size_t s
|
|
1751
1798
|
parse_inline(inter, rndr, work->data, work->size);
|
1752
1799
|
}
|
1753
1800
|
|
1801
|
+
if (!has_trailing_empty)
|
1802
|
+
*flags &= ~MKD_LI_BLOCK;
|
1803
|
+
|
1754
1804
|
/* render of li itself */
|
1755
1805
|
if (rndr->cb.listitem)
|
1756
1806
|
rndr->cb.listitem(ob, inter, *flags, rndr->opaque);
|
@@ -1989,8 +2039,9 @@ parse_table_row(
|
|
1989
2039
|
int *col_data,
|
1990
2040
|
int header_flag)
|
1991
2041
|
{
|
1992
|
-
size_t i = 0, col;
|
2042
|
+
size_t i = 0, j, code_end, col;
|
1993
2043
|
struct buf *row_work = 0;
|
2044
|
+
int nb = 0, is_escaped = 0;
|
1994
2045
|
|
1995
2046
|
if (!rndr->cb.table_cell || !rndr->cb.table_row)
|
1996
2047
|
return;
|
@@ -2011,8 +2062,35 @@ parse_table_row(
|
|
2011
2062
|
|
2012
2063
|
cell_start = i;
|
2013
2064
|
|
2014
|
-
|
2015
|
-
|
2065
|
+
/* find the | marking the end of this cell */
|
2066
|
+
while (i < size) {
|
2067
|
+
if (!is_escaped && data[i] == '|')
|
2068
|
+
break;
|
2069
|
+
|
2070
|
+
/* find code spans because they can contain |s */
|
2071
|
+
if (!is_escaped && data[i] == '`') {
|
2072
|
+
for (nb=0; i < size && data[i] == '`'; i++)
|
2073
|
+
nb++;
|
2074
|
+
|
2075
|
+
for (j=0, code_end=i; code_end < size && j < nb; code_end++) {
|
2076
|
+
if (data[code_end] == '`') j++;
|
2077
|
+
else j = 0;
|
2078
|
+
}
|
2079
|
+
|
2080
|
+
/* bail if there's no matching delimiter */
|
2081
|
+
if (j < nb) {
|
2082
|
+
i++;
|
2083
|
+
continue;
|
2084
|
+
}
|
2085
|
+
|
2086
|
+
i = code_end;
|
2087
|
+
is_escaped = 0;
|
2088
|
+
}
|
2089
|
+
else {
|
2090
|
+
is_escaped = !is_escaped && data[i] == '\\';
|
2091
|
+
i++;
|
2092
|
+
}
|
2093
|
+
}
|
2016
2094
|
|
2017
2095
|
cell_end = i - 1;
|
2018
2096
|
|
@@ -2282,11 +2360,11 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2282
2360
|
i++;
|
2283
2361
|
if (i >= end || data[i] != ':') return 0;
|
2284
2362
|
i++;
|
2285
|
-
while (i < end && data[i] == ' ') i++;
|
2363
|
+
while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
|
2286
2364
|
if (i < end && (data[i] == '\n' || data[i] == '\r')) {
|
2287
2365
|
i++;
|
2288
2366
|
if (i < end && data[i] == '\r' && data[i - 1] == '\n') i++; }
|
2289
|
-
while (i < end && data[i] == ' ') i++;
|
2367
|
+
while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
|
2290
2368
|
if (i >= end) return 0;
|
2291
2369
|
|
2292
2370
|
/* link: whitespace-free sequence, optionally between angle brackets */
|
@@ -2295,14 +2373,14 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2295
2373
|
|
2296
2374
|
link_offset = i;
|
2297
2375
|
|
2298
|
-
while (i < end && data[i] != ' ' && data[i] != '\n' && data[i] != '\r')
|
2376
|
+
while (i < end && data[i] != ' ' && data[i] != '\t' && data[i] != '\n' && data[i] != '\r')
|
2299
2377
|
i++;
|
2300
2378
|
|
2301
2379
|
if (data[i - 1] == '>') link_end = i - 1;
|
2302
2380
|
else link_end = i;
|
2303
2381
|
|
2304
2382
|
/* optional spacer: (space | tab)* (newline | '\'' | '"' | '(' ) */
|
2305
|
-
while (i < end && data[i] == ' ') i++;
|
2383
|
+
while (i < end && (data[i] == ' ' || data[i] == '\t')) i++;
|
2306
2384
|
if (i < end && data[i] != '\n' && data[i] != '\r'
|
2307
2385
|
&& data[i] != '\'' && data[i] != '"' && data[i] != '(')
|
2308
2386
|
return 0;
|
@@ -2315,7 +2393,7 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2315
2393
|
/* optional (space|tab)* spacer after a newline */
|
2316
2394
|
if (line_end) {
|
2317
2395
|
i = line_end + 1;
|
2318
|
-
while (i < end && data[i] == ' ') i++; }
|
2396
|
+
while (i < end && (data[i] == ' ' || data[i] == '\t')) i++; }
|
2319
2397
|
|
2320
2398
|
/* optional title: any non-newline sequence enclosed in '"()
|
2321
2399
|
alone on its line */
|
@@ -2331,7 +2409,7 @@ is_ref(const uint8_t *data, size_t beg, size_t end, size_t *last, struct link_re
|
|
2331
2409
|
else title_end = i;
|
2332
2410
|
/* stepping back */
|
2333
2411
|
i -= 1;
|
2334
|
-
while (i > title_offset && data[i] == ' ')
|
2412
|
+
while (i > title_offset && (data[i] == ' ' || data[i] == '\t'))
|
2335
2413
|
i -= 1;
|
2336
2414
|
if (i > title_offset
|
2337
2415
|
&& (data[i] == '\'' || data[i] == '"' || data[i] == ')')) {
|
@@ -2460,7 +2538,8 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2460
2538
|
static const char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
|
2461
2539
|
|
2462
2540
|
struct buf *text;
|
2463
|
-
size_t beg, end;
|
2541
|
+
size_t beg, end, expanded_beg;
|
2542
|
+
int in_code_block = 0;
|
2464
2543
|
|
2465
2544
|
text = bufnew(64);
|
2466
2545
|
if (!text)
|
@@ -2481,7 +2560,7 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2481
2560
|
beg += 3;
|
2482
2561
|
|
2483
2562
|
while (beg < doc_size) /* iterating over lines */
|
2484
|
-
if (is_ref(document, beg, doc_size, &end, md->refs))
|
2563
|
+
if (!in_code_block && is_ref(document, beg, doc_size, &end, md->refs))
|
2485
2564
|
beg = end;
|
2486
2565
|
else { /* skipping to the next line */
|
2487
2566
|
end = beg;
|
@@ -2489,9 +2568,17 @@ sd_markdown_render(struct buf *ob, const uint8_t *document, size_t doc_size, str
|
|
2489
2568
|
end++;
|
2490
2569
|
|
2491
2570
|
/* adding the line body if present */
|
2492
|
-
if (end > beg)
|
2571
|
+
if (end > beg) {
|
2572
|
+
expanded_beg = text->size;
|
2573
|
+
|
2493
2574
|
expand_tabs(text, document + beg, end - beg);
|
2494
2575
|
|
2576
|
+
/* check for a fenced code block */
|
2577
|
+
if ((md->ext_flags & MKDEXT_FENCED_CODE) != 0 &&
|
2578
|
+
is_codefence(text->data + expanded_beg, text->size - expanded_beg, NULL) != 0)
|
2579
|
+
in_code_block = !in_code_block;
|
2580
|
+
}
|
2581
|
+
|
2495
2582
|
while (end < doc_size && (document[end] == '\n' || document[end] == '\r')) {
|
2496
2583
|
/* add one \n per newline */
|
2497
2584
|
if (document[end] == '\n' || (end + 1 < doc_size && document[end + 1] != '\n'))
|
data/ext/markdown/plaintext.c
CHANGED
@@ -109,12 +109,6 @@ rndr_paragraph(struct buf *ob, const struct buf *text, void *opaque)
|
|
109
109
|
plaintext_block(ob, text);
|
110
110
|
}
|
111
111
|
|
112
|
-
static void
|
113
|
-
rndr_raw_block(struct buf *ob, const struct buf *text, void *opaque)
|
114
|
-
{
|
115
|
-
/* NO OP */
|
116
|
-
}
|
117
|
-
|
118
112
|
static void
|
119
113
|
rndr_hrule(struct buf *ob, void *opaque)
|
120
114
|
{
|
@@ -159,7 +153,7 @@ sdtext_renderer(struct sd_callbacks *callbacks)
|
|
159
153
|
static const struct sd_callbacks cb_default = {
|
160
154
|
rndr_blockcode,
|
161
155
|
rndr_blockquote,
|
162
|
-
|
156
|
+
NULL,
|
163
157
|
rndr_header,
|
164
158
|
rndr_hrule,
|
165
159
|
rndr_list,
|
data/github-markdown.gemspec
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
Gem::Specification.new do |s|
|
3
3
|
s.name = 'github-markdown'
|
4
|
-
s.version = '0.
|
5
|
-
s.summary = 'The Markdown parser for GitHub.com'
|
6
|
-
s.description = 'Self-contained Markdown parser for GitHub, with all our custom extensions'
|
7
|
-
s.
|
8
|
-
s.email = 'vicent@github.com'
|
9
|
-
s.homepage = 'http://github.github.com/github-flavored-markdown/'
|
4
|
+
s.version = '0.6.9'
|
5
|
+
s.summary = 'THIS GEM IS NOT MAINTAINED AND NOT SUPPORTED. The Markdown parser for GitHub.com'
|
6
|
+
s.description = 'THIS GEM IS NOT MAINTAINED AND NOT SUPPORTED. Self-contained Markdown parser for GitHub, with all our custom extensions'
|
7
|
+
s.license = 'MIT'
|
10
8
|
s.authors = ['GitHub, Inc']
|
11
9
|
# = MANIFEST =
|
12
10
|
s.files = %w[
|
@@ -38,5 +36,4 @@ Gem::Specification.new do |s|
|
|
38
36
|
s.test_files = ["test/gfm_test.rb"]
|
39
37
|
s.extensions = ["ext/markdown/extconf.rb"]
|
40
38
|
s.require_paths = ["lib"]
|
41
|
-
s.add_development_dependency "rake-compiler"
|
42
39
|
end
|
data/test/gfm_test.rb
CHANGED
@@ -16,6 +16,33 @@ def html_equal(html_a, html_b)
|
|
16
16
|
end
|
17
17
|
|
18
18
|
class GFMBasicTest < Test::Unit::TestCase
|
19
|
+
Dir['test/fixtures/*.text', 'test/fixtures/Markdown_Redcarpet/**/*.text'].each do |md_file|
|
20
|
+
dirname = File.dirname(md_file)
|
21
|
+
markup = md_file.split('/').last.gsub(/\.text/, '').gsub(/(\s+)/, "_")
|
22
|
+
define_method "test_#{dirname}_#{markup}" do
|
23
|
+
source = File.read(md_file)
|
24
|
+
|
25
|
+
expected_file = "#{dirname}/#{markup}.html"
|
26
|
+
expected = File.read(expected_file).rstrip
|
27
|
+
actual = GitHub::Markdown.render(source).rstrip
|
28
|
+
|
29
|
+
if source != expected
|
30
|
+
assert(source != actual, "#{markup} did not render anything")
|
31
|
+
end
|
32
|
+
|
33
|
+
diff = IO.popen("diff -u - #{expected_file}", 'r+') do |f|
|
34
|
+
f.write actual
|
35
|
+
f.close_write
|
36
|
+
f.read
|
37
|
+
end
|
38
|
+
|
39
|
+
assert expected == actual, <<message
|
40
|
+
#{File.basename expected_file}'s contents don't match command output:
|
41
|
+
#{diff}
|
42
|
+
message
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
19
46
|
def test_that_render_works
|
20
47
|
GitHub::Markdown.to_html("Hello **world**!", :gfm)
|
21
48
|
end
|
@@ -50,7 +77,7 @@ class GFMBasicTest < Test::Unit::TestCase
|
|
50
77
|
parsed = Nokogiri::HTML::DocumentFragment.parse(html)
|
51
78
|
|
52
79
|
items.inject(parsed) do |node, expected_item|
|
53
|
-
child = node.
|
80
|
+
child = node.xpath('.//ul/li')
|
54
81
|
child_item = child.children.detect{|e| e.text?}.text.strip
|
55
82
|
assert_equal expected_item, child_item
|
56
83
|
node = child
|
metadata
CHANGED
@@ -1,46 +1,23 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: github-markdown
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
prerelease:
|
6
|
-
segments:
|
7
|
-
- 0
|
8
|
-
- 5
|
9
|
-
- 2
|
10
|
-
version: 0.5.2
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.6.9
|
11
5
|
platform: ruby
|
12
|
-
authors:
|
6
|
+
authors:
|
13
7
|
- GitHub, Inc
|
14
8
|
autorequire:
|
15
9
|
bindir: bin
|
16
10
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
name: rake-compiler
|
23
|
-
prerelease: false
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ">="
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
hash: 3
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
33
|
-
type: :development
|
34
|
-
version_requirements: *id001
|
35
|
-
description: Self-contained Markdown parser for GitHub, with all our custom extensions
|
36
|
-
email: vicent@github.com
|
11
|
+
date: 2015-08-10 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: THIS GEM IS NOT MAINTAINED AND NOT SUPPORTED. Self-contained Markdown
|
14
|
+
parser for GitHub, with all our custom extensions
|
15
|
+
email:
|
37
16
|
executables: []
|
38
|
-
|
39
|
-
extensions:
|
17
|
+
extensions:
|
40
18
|
- ext/markdown/extconf.rb
|
41
19
|
extra_rdoc_files: []
|
42
|
-
|
43
|
-
files:
|
20
|
+
files:
|
44
21
|
- Rakefile
|
45
22
|
- bin/gfm
|
46
23
|
- ext/markdown/autolink.c
|
@@ -64,39 +41,29 @@ files:
|
|
64
41
|
- github-markdown.gemspec
|
65
42
|
- lib/github/markdown.rb
|
66
43
|
- test/gfm_test.rb
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
44
|
+
homepage:
|
45
|
+
licenses:
|
46
|
+
- MIT
|
47
|
+
metadata: {}
|
71
48
|
post_install_message:
|
72
49
|
rdoc_options: []
|
73
|
-
|
74
|
-
require_paths:
|
50
|
+
require_paths:
|
75
51
|
- lib
|
76
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
77
|
-
|
78
|
-
requirements:
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
requirements:
|
79
54
|
- - ">="
|
80
|
-
- !ruby/object:Gem::Version
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
version: "0"
|
85
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
|
-
none: false
|
87
|
-
requirements:
|
55
|
+
- !ruby/object:Gem::Version
|
56
|
+
version: '0'
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
88
59
|
- - ">="
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
segments:
|
92
|
-
- 0
|
93
|
-
version: "0"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
94
62
|
requirements: []
|
95
|
-
|
96
63
|
rubyforge_project:
|
97
|
-
rubygems_version:
|
64
|
+
rubygems_version: 2.2.3
|
98
65
|
signing_key:
|
99
|
-
specification_version:
|
100
|
-
summary: The Markdown parser for GitHub.com
|
101
|
-
test_files:
|
66
|
+
specification_version: 4
|
67
|
+
summary: THIS GEM IS NOT MAINTAINED AND NOT SUPPORTED. The Markdown parser for GitHub.com
|
68
|
+
test_files:
|
102
69
|
- test/gfm_test.rb
|