github-markdown 0.5.2 → 0.6.9
Sign up to get free protection for your applications and to get access to all the features.
- 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
|