md4c 0.0.1
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/.clang-format +1 -0
- data/.envrc +8 -0
- data/CHANGELOG.md +7 -0
- data/COPYING +674 -0
- data/README.md +57 -0
- data/Rakefile +22 -0
- data/compile_flags.txt +5 -0
- data/ext/md4c/extconf.rb +35 -0
- data/ext/md4c/rb_md4c.c +741 -0
- data/ext/md4c/rb_md4c.h +25 -0
- data/lib/md4c/version.rb +22 -0
- data/lib/md4c.rb +148 -0
- data/sig/md4c.rbs +4 -0
- metadata +57 -0
data/ext/md4c/rb_md4c.c
ADDED
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (C) 2026 gemmaro
|
|
3
|
+
*
|
|
4
|
+
* This program is free software: you can redistribute it and/or modify
|
|
5
|
+
* it under the terms of the GNU General Public License as published by
|
|
6
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
7
|
+
* (at your option) any later version.
|
|
8
|
+
*
|
|
9
|
+
* This program is distributed in the hope that it will be useful,
|
|
10
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
11
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
12
|
+
* GNU General Public License for more details.
|
|
13
|
+
*
|
|
14
|
+
* You should have received a copy of the GNU General Public License
|
|
15
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
#include "rb_md4c.h"
|
|
19
|
+
#include <md4c-html.h>
|
|
20
|
+
#include <md4c.h>
|
|
21
|
+
#include <ruby.h>
|
|
22
|
+
#include <stddef.h>
|
|
23
|
+
|
|
24
|
+
/* rbm_ prefix means "Ruby MD4C". */
|
|
25
|
+
|
|
26
|
+
static VALUE rb_mMD4C, rbm_cParser, rbm_cStringAttribute, /* blocks --> */
|
|
27
|
+
rbm_cUnorderedList, rbm_cOrderedList, rbm_cListItem, rbm_cHeading,
|
|
28
|
+
rbm_cCodeBlock, rbm_cTable, rbm_cTableHeader, rbm_cTableData,
|
|
29
|
+
/* spans --> */ rbm_cAnchor, rbm_cImage, rbm_cWikiLink,
|
|
30
|
+
/* texts --> */ rbm_cNormalText, rbm_cNullChar, rbm_cHardBreak,
|
|
31
|
+
rbm_cSoftBreak, rbm_cEntity, rbm_cCodeText, rbm_cHTMLText,
|
|
32
|
+
rbm_cLaTeXMathText, /* error --> */ rbm_eError;
|
|
33
|
+
|
|
34
|
+
struct rbm_cParser
|
|
35
|
+
{
|
|
36
|
+
unsigned flags;
|
|
37
|
+
VALUE on_enter_block, on_leave_block, on_enter_span, on_leave_span, on_text;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
static size_t
|
|
41
|
+
rbm_cParser_size (const void *ptr)
|
|
42
|
+
{
|
|
43
|
+
return sizeof (struct rbm_cParser);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static void
|
|
47
|
+
rbm_cParser_mark (void *ptr)
|
|
48
|
+
{
|
|
49
|
+
struct rbm_cParser *p = ptr;
|
|
50
|
+
rb_gc_mark_movable (p->on_enter_block);
|
|
51
|
+
rb_gc_mark_movable (p->on_leave_block);
|
|
52
|
+
rb_gc_mark_movable (p->on_enter_span);
|
|
53
|
+
rb_gc_mark_movable (p->on_leave_span);
|
|
54
|
+
rb_gc_mark_movable (p->on_text);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static void
|
|
58
|
+
rbm_cParser_compact (void *ptr)
|
|
59
|
+
{
|
|
60
|
+
struct rbm_cParser *p = ptr;
|
|
61
|
+
p->on_enter_block = rb_gc_location (p->on_enter_block);
|
|
62
|
+
p->on_leave_block = rb_gc_location (p->on_leave_block);
|
|
63
|
+
p->on_enter_span = rb_gc_location (p->on_enter_span);
|
|
64
|
+
p->on_leave_span = rb_gc_location (p->on_leave_span);
|
|
65
|
+
p->on_text = rb_gc_location (p->on_text);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
static const rb_data_type_t rbm_cParser_type
|
|
69
|
+
= { .wrap_struct_name = "MD4C parser",
|
|
70
|
+
.function = { .dfree = RUBY_DEFAULT_FREE,
|
|
71
|
+
.dmark = rbm_cParser_mark,
|
|
72
|
+
.dcompact = rbm_cParser_compact,
|
|
73
|
+
.dsize = rbm_cParser_size },
|
|
74
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY };
|
|
75
|
+
|
|
76
|
+
VALUE
|
|
77
|
+
rbm_cParser_alloc (VALUE self)
|
|
78
|
+
{
|
|
79
|
+
struct rbm_cParser *p = ruby_xmalloc (sizeof (struct rbm_cParser));
|
|
80
|
+
return rb_data_typed_object_wrap (rbm_cParser, p, &rbm_cParser_type);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#define PARSER_FLAGS \
|
|
84
|
+
rb_intern ("collapse_whitespace"), rb_intern ("permissive_atx_headers"), \
|
|
85
|
+
rb_intern ("permissive_url_autolinks"), \
|
|
86
|
+
rb_intern ("permissive_email_autolinks"), \
|
|
87
|
+
rb_intern ("no_indented_codeblocks"), rb_intern ("no_html_blocks"), \
|
|
88
|
+
rb_intern ("no_html_spans"), rb_intern ("tables"), \
|
|
89
|
+
rb_intern ("strikethrough"), rb_intern ("permissive_www_autolinks"), \
|
|
90
|
+
rb_intern ("task_lists"), rb_intern ("latex_math_spans"), \
|
|
91
|
+
rb_intern ("wiki_links"), rb_intern ("underline"), \
|
|
92
|
+
rb_intern ("hard_soft_breaks"), rb_intern ("permissive_autolinks"), \
|
|
93
|
+
rb_intern ("no_html"), rb_intern ("commonmark"), rb_intern ("github")
|
|
94
|
+
|
|
95
|
+
static unsigned
|
|
96
|
+
rbm_get_parser_flags (ID kwargs_vals[])
|
|
97
|
+
{
|
|
98
|
+
unsigned flags = 0;
|
|
99
|
+
|
|
100
|
+
if (!RB_UNDEF_P (kwargs_vals[0]) && RB_TEST (kwargs_vals[0]))
|
|
101
|
+
flags |= MD_FLAG_COLLAPSEWHITESPACE;
|
|
102
|
+
|
|
103
|
+
if (!RB_UNDEF_P (kwargs_vals[1]) && RB_TEST (kwargs_vals[1]))
|
|
104
|
+
flags |= MD_FLAG_PERMISSIVEATXHEADERS;
|
|
105
|
+
|
|
106
|
+
if (!RB_UNDEF_P (kwargs_vals[2]) && RB_TEST (kwargs_vals[2]))
|
|
107
|
+
flags |= MD_FLAG_PERMISSIVEURLAUTOLINKS;
|
|
108
|
+
|
|
109
|
+
if (!RB_UNDEF_P (kwargs_vals[3]) && RB_TEST (kwargs_vals[3]))
|
|
110
|
+
flags |= MD_FLAG_PERMISSIVEEMAILAUTOLINKS;
|
|
111
|
+
|
|
112
|
+
if (!RB_UNDEF_P (kwargs_vals[4]) && RB_TEST (kwargs_vals[4]))
|
|
113
|
+
flags |= MD_FLAG_NOINDENTEDCODEBLOCKS;
|
|
114
|
+
|
|
115
|
+
if (!RB_UNDEF_P (kwargs_vals[5]) && RB_TEST (kwargs_vals[5]))
|
|
116
|
+
flags |= MD_FLAG_NOHTMLBLOCKS;
|
|
117
|
+
|
|
118
|
+
if (!RB_UNDEF_P (kwargs_vals[6]) && RB_TEST (kwargs_vals[6]))
|
|
119
|
+
flags |= MD_FLAG_NOHTMLSPANS;
|
|
120
|
+
|
|
121
|
+
if (!RB_UNDEF_P (kwargs_vals[7]) && RB_TEST (kwargs_vals[7]))
|
|
122
|
+
flags |= MD_FLAG_TABLES;
|
|
123
|
+
|
|
124
|
+
if (!RB_UNDEF_P (kwargs_vals[8]) && RB_TEST (kwargs_vals[8]))
|
|
125
|
+
flags |= MD_FLAG_STRIKETHROUGH;
|
|
126
|
+
|
|
127
|
+
if (!RB_UNDEF_P (kwargs_vals[9]) && RB_TEST (kwargs_vals[9]))
|
|
128
|
+
flags |= MD_FLAG_PERMISSIVEWWWAUTOLINKS;
|
|
129
|
+
|
|
130
|
+
if (!RB_UNDEF_P (kwargs_vals[10]) && RB_TEST (kwargs_vals[10]))
|
|
131
|
+
flags |= MD_FLAG_TASKLISTS;
|
|
132
|
+
|
|
133
|
+
if (!RB_UNDEF_P (kwargs_vals[11]) && RB_TEST (kwargs_vals[11]))
|
|
134
|
+
flags |= MD_FLAG_LATEXMATHSPANS;
|
|
135
|
+
|
|
136
|
+
if (!RB_UNDEF_P (kwargs_vals[12]) && RB_TEST (kwargs_vals[12]))
|
|
137
|
+
flags |= MD_FLAG_WIKILINKS;
|
|
138
|
+
|
|
139
|
+
if (!RB_UNDEF_P (kwargs_vals[13]) && RB_TEST (kwargs_vals[13]))
|
|
140
|
+
flags |= MD_FLAG_UNDERLINE;
|
|
141
|
+
|
|
142
|
+
if (!RB_UNDEF_P (kwargs_vals[14]) && RB_TEST (kwargs_vals[14]))
|
|
143
|
+
flags |= MD_FLAG_HARD_SOFT_BREAKS;
|
|
144
|
+
|
|
145
|
+
if (!RB_UNDEF_P (kwargs_vals[15]) && RB_TEST (kwargs_vals[15]))
|
|
146
|
+
flags |= MD_FLAG_PERMISSIVEAUTOLINKS;
|
|
147
|
+
|
|
148
|
+
if (!RB_UNDEF_P (kwargs_vals[16]) && RB_TEST (kwargs_vals[16]))
|
|
149
|
+
flags |= MD_FLAG_NOHTML;
|
|
150
|
+
|
|
151
|
+
if (!RB_UNDEF_P (kwargs_vals[17]) && RB_TEST (kwargs_vals[17]))
|
|
152
|
+
flags |= MD_DIALECT_COMMONMARK;
|
|
153
|
+
|
|
154
|
+
if (!RB_UNDEF_P (kwargs_vals[18]) && RB_TEST (kwargs_vals[18]))
|
|
155
|
+
flags |= MD_DIALECT_GITHUB;
|
|
156
|
+
|
|
157
|
+
return flags;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
static VALUE
|
|
161
|
+
rbm_cParser_m_initialize (int argc, VALUE *argv, VALUE self)
|
|
162
|
+
{
|
|
163
|
+
VALUE kwargs;
|
|
164
|
+
rb_scan_args (argc, argv, ":", &kwargs);
|
|
165
|
+
ID kwargs_ids[] = { PARSER_FLAGS };
|
|
166
|
+
VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
|
|
167
|
+
rb_get_kwargs (kwargs, kwargs_ids, 0,
|
|
168
|
+
sizeof (kwargs_ids) / sizeof (kwargs_ids[0]), kwargs_vals);
|
|
169
|
+
|
|
170
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
171
|
+
*p = (struct rbm_cParser){ .flags = rbm_get_parser_flags (kwargs_vals) };
|
|
172
|
+
return self;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
static VALUE
|
|
176
|
+
rbm_cParser_m_on_enter_block (VALUE self)
|
|
177
|
+
{
|
|
178
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
179
|
+
return p->on_enter_block = rb_block_proc ();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
static VALUE
|
|
183
|
+
rbm_cParser_m_on_leave_block (VALUE self)
|
|
184
|
+
{
|
|
185
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
186
|
+
return p->on_leave_block = rb_block_proc ();
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
static VALUE
|
|
190
|
+
rbm_cParser_m_on_enter_span (VALUE self)
|
|
191
|
+
{
|
|
192
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
193
|
+
return p->on_enter_span = rb_block_proc ();
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static VALUE
|
|
197
|
+
rbm_cParser_m_on_leave_span (VALUE self)
|
|
198
|
+
{
|
|
199
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
200
|
+
return p->on_leave_span = rb_block_proc ();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
static VALUE
|
|
204
|
+
rbm_cParser_m_on_text (VALUE self)
|
|
205
|
+
{
|
|
206
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
207
|
+
return p->on_text = rb_block_proc ();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static size_t
|
|
211
|
+
rbm_cStringAttribute_size (const void *ptr)
|
|
212
|
+
{
|
|
213
|
+
return sizeof (MD_ATTRIBUTE);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
static const rb_data_type_t rbm_cStringAttribute_type
|
|
217
|
+
= { .wrap_struct_name = "MD4C string attribute",
|
|
218
|
+
.function
|
|
219
|
+
= { .dfree = RUBY_DEFAULT_FREE, .dsize = rbm_cStringAttribute_size },
|
|
220
|
+
.flags = RUBY_TYPED_FREE_IMMEDIATELY };
|
|
221
|
+
|
|
222
|
+
static VALUE
|
|
223
|
+
rbm_cStringAttribute_alloc (VALUE self)
|
|
224
|
+
{
|
|
225
|
+
MD_BLOCK_CODE_DETAIL *attr = ruby_xmalloc (sizeof (MD_ATTRIBUTE));
|
|
226
|
+
return rb_data_typed_object_wrap (rbm_cStringAttribute, attr,
|
|
227
|
+
&rbm_cStringAttribute_type);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
static VALUE
|
|
231
|
+
rbm_get_text_class (MD_TEXTTYPE type)
|
|
232
|
+
{
|
|
233
|
+
switch (type)
|
|
234
|
+
{
|
|
235
|
+
case MD_TEXT_NORMAL:
|
|
236
|
+
return rbm_cNormalText;
|
|
237
|
+
|
|
238
|
+
case MD_TEXT_NULLCHAR:
|
|
239
|
+
return rbm_cNullChar;
|
|
240
|
+
|
|
241
|
+
case MD_TEXT_BR:
|
|
242
|
+
return rbm_cSoftBreak;
|
|
243
|
+
|
|
244
|
+
case MD_TEXT_SOFTBR:
|
|
245
|
+
return rbm_cHardBreak;
|
|
246
|
+
|
|
247
|
+
case MD_TEXT_ENTITY:
|
|
248
|
+
return rbm_cEntity;
|
|
249
|
+
|
|
250
|
+
case MD_TEXT_CODE:
|
|
251
|
+
return rbm_cCodeText;
|
|
252
|
+
|
|
253
|
+
case MD_TEXT_HTML:
|
|
254
|
+
return rbm_cHTMLText;
|
|
255
|
+
|
|
256
|
+
case MD_TEXT_LATEXMATH:
|
|
257
|
+
return rbm_cLaTeXMathText;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
static VALUE
|
|
262
|
+
rbm_cStringAttribute_m_each (VALUE self)
|
|
263
|
+
{
|
|
264
|
+
RETURN_ENUMERATOR (self, 0, NULL);
|
|
265
|
+
|
|
266
|
+
MD_ATTRIBUTE *attr = RTYPEDDATA_DATA (self);
|
|
267
|
+
size_t index = 0;
|
|
268
|
+
while (attr->substr_offsets[index] >= attr->size)
|
|
269
|
+
{
|
|
270
|
+
VALUE klass = rbm_get_text_class (attr->substr_types[index]);
|
|
271
|
+
VALUE substr = rb_class_new_instance (0, NULL, klass);
|
|
272
|
+
VALUE content = rb_str_new (attr->text + attr->substr_offsets[index],
|
|
273
|
+
attr->substr_offsets[index + 1]
|
|
274
|
+
- attr->substr_offsets[index]);
|
|
275
|
+
rb_iv_set (substr, "@content", content);
|
|
276
|
+
rb_yield (substr);
|
|
277
|
+
}
|
|
278
|
+
return RUBY_Qnil;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
static VALUE
|
|
282
|
+
rbm_callback_block_arguments (MD_BLOCKTYPE type, void *detail)
|
|
283
|
+
{
|
|
284
|
+
VALUE arg;
|
|
285
|
+
switch (type)
|
|
286
|
+
{
|
|
287
|
+
case MD_BLOCK_DOC:
|
|
288
|
+
arg = rb_id2sym (rb_intern ("document"));
|
|
289
|
+
break;
|
|
290
|
+
|
|
291
|
+
case MD_BLOCK_QUOTE:
|
|
292
|
+
arg = rb_id2sym (rb_intern ("quote"));
|
|
293
|
+
break;
|
|
294
|
+
|
|
295
|
+
case MD_BLOCK_UL:
|
|
296
|
+
{
|
|
297
|
+
arg = rb_class_new_instance (0, NULL, rbm_cUnorderedList);
|
|
298
|
+
MD_BLOCK_UL_DETAIL *d = detail;
|
|
299
|
+
rb_iv_set (arg, "@tight", d->is_tight ? RUBY_Qtrue : RUBY_Qfalse);
|
|
300
|
+
|
|
301
|
+
VALUE mark = rb_str_new (&d->mark, 1);
|
|
302
|
+
rb_iv_set (arg, "@mark", mark);
|
|
303
|
+
break;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case MD_BLOCK_OL:
|
|
307
|
+
{
|
|
308
|
+
arg = rb_class_new_instance (0, NULL, rbm_cOrderedList);
|
|
309
|
+
MD_BLOCK_OL_DETAIL *d = detail;
|
|
310
|
+
|
|
311
|
+
VALUE start = rb_uint2num_inline (d->start);
|
|
312
|
+
rb_iv_set (arg, "@start", start);
|
|
313
|
+
|
|
314
|
+
rb_iv_set (arg, "@tight", d->is_tight ? RUBY_Qtrue : RUBY_Qfalse);
|
|
315
|
+
|
|
316
|
+
VALUE delimiter = rb_str_new (&d->mark_delimiter, 1);
|
|
317
|
+
rb_iv_set (arg, "@mark_delimiter", delimiter);
|
|
318
|
+
break;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
case MD_BLOCK_LI:
|
|
322
|
+
{
|
|
323
|
+
arg = rb_class_new_instance (0, NULL, rbm_cListItem);
|
|
324
|
+
MD_BLOCK_LI_DETAIL *d = detail;
|
|
325
|
+
rb_iv_set (arg, "@task", d->is_task ? RUBY_Qtrue : RUBY_Qfalse);
|
|
326
|
+
|
|
327
|
+
VALUE task_mark
|
|
328
|
+
= d->is_task ? rb_str_new (&d->task_mark, 1) : RUBY_Qnil;
|
|
329
|
+
rb_iv_set (arg, "@task_mark", task_mark);
|
|
330
|
+
|
|
331
|
+
VALUE offset = rb_uint2num_inline (d->task_mark_offset);
|
|
332
|
+
rb_iv_set (arg, "@task_mark_offset", offset);
|
|
333
|
+
break;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
case MD_BLOCK_HR:
|
|
337
|
+
/* https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/hr
|
|
338
|
+
*/
|
|
339
|
+
arg = rb_id2sym (rb_intern ("thematic_break"));
|
|
340
|
+
break;
|
|
341
|
+
|
|
342
|
+
case MD_BLOCK_H:
|
|
343
|
+
{
|
|
344
|
+
arg = rb_class_new_instance (0, NULL, rbm_cHeading);
|
|
345
|
+
MD_BLOCK_H_DETAIL *d = detail;
|
|
346
|
+
|
|
347
|
+
VALUE level = rb_uint2num_inline (d->level);
|
|
348
|
+
rb_iv_set (arg, "@level", level);
|
|
349
|
+
break;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
case MD_BLOCK_CODE:
|
|
353
|
+
{
|
|
354
|
+
arg = rb_class_new_instance (0, NULL, rbm_cCodeBlock);
|
|
355
|
+
MD_BLOCK_CODE_DETAIL *d = detail;
|
|
356
|
+
|
|
357
|
+
VALUE info = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
358
|
+
RTYPEDDATA_DATA (info) = &d->info;
|
|
359
|
+
rb_iv_set (arg, "@info", info);
|
|
360
|
+
|
|
361
|
+
VALUE lang = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
362
|
+
RTYPEDDATA_DATA (lang) = &d->lang;
|
|
363
|
+
rb_iv_set (arg, "@lang", lang);
|
|
364
|
+
|
|
365
|
+
VALUE fence
|
|
366
|
+
= d->fence_char ? rb_str_new (&d->fence_char, 1) : RUBY_Qnil;
|
|
367
|
+
rb_iv_set (arg, "@fence_char", fence);
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
case MD_BLOCK_HTML:
|
|
372
|
+
arg = rb_id2sym (rb_intern ("html"));
|
|
373
|
+
break;
|
|
374
|
+
|
|
375
|
+
case MD_BLOCK_P:
|
|
376
|
+
arg = rb_id2sym (rb_intern ("paragraph"));
|
|
377
|
+
break;
|
|
378
|
+
|
|
379
|
+
case MD_BLOCK_TABLE:
|
|
380
|
+
{
|
|
381
|
+
arg = rb_class_new_instance (0, NULL, rbm_cTable);
|
|
382
|
+
MD_BLOCK_TABLE_DETAIL *d = detail;
|
|
383
|
+
|
|
384
|
+
VALUE col = rb_uint2num_inline (d->col_count);
|
|
385
|
+
rb_iv_set (arg, "@col_count", col);
|
|
386
|
+
|
|
387
|
+
VALUE head = rb_uint2num_inline (d->head_row_count);
|
|
388
|
+
rb_iv_set (arg, "@head_row_count", head);
|
|
389
|
+
|
|
390
|
+
VALUE body = rb_uint2num_inline (d->body_row_count);
|
|
391
|
+
rb_iv_set (arg, "@body_row_count", body);
|
|
392
|
+
break;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
case MD_BLOCK_THEAD:
|
|
396
|
+
/* <thead> HTML table head element
|
|
397
|
+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/thead
|
|
398
|
+
*/
|
|
399
|
+
arg = rb_id2sym (rb_intern ("table_head"));
|
|
400
|
+
break;
|
|
401
|
+
|
|
402
|
+
case MD_BLOCK_TBODY:
|
|
403
|
+
arg = rb_id2sym (rb_intern ("table_body"));
|
|
404
|
+
break;
|
|
405
|
+
|
|
406
|
+
case MD_BLOCK_TR:
|
|
407
|
+
arg = rb_id2sym (rb_intern ("table_row"));
|
|
408
|
+
break;
|
|
409
|
+
|
|
410
|
+
case MD_BLOCK_TH:
|
|
411
|
+
arg = rb_class_new_instance (0, NULL, rbm_cTableHeader);
|
|
412
|
+
goto table_data;
|
|
413
|
+
|
|
414
|
+
case MD_BLOCK_TD:
|
|
415
|
+
arg = rb_class_new_instance (0, NULL, rbm_cTableData);
|
|
416
|
+
goto table_data;
|
|
417
|
+
|
|
418
|
+
table_data:
|
|
419
|
+
{
|
|
420
|
+
VALUE align = RUBY_Qnil;
|
|
421
|
+
MD_BLOCK_TD_DETAIL *d = detail;
|
|
422
|
+
switch (d->align)
|
|
423
|
+
{
|
|
424
|
+
case MD_ALIGN_DEFAULT:
|
|
425
|
+
break;
|
|
426
|
+
|
|
427
|
+
case MD_ALIGN_LEFT:
|
|
428
|
+
{
|
|
429
|
+
align = rb_id2sym (rb_intern ("left"));
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
case MD_ALIGN_CENTER:
|
|
434
|
+
{
|
|
435
|
+
align = rb_id2sym (rb_intern ("center"));
|
|
436
|
+
break;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
case MD_ALIGN_RIGHT:
|
|
440
|
+
{
|
|
441
|
+
align = rb_id2sym (rb_intern ("right"));
|
|
442
|
+
break;
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
rb_iv_set (arg, "@align", align);
|
|
447
|
+
break;
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
VALUE args = rb_ary_new ();
|
|
452
|
+
rb_ary_push (args, arg);
|
|
453
|
+
return args;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
static int
|
|
457
|
+
rbm_callback_enter_block (MD_BLOCKTYPE type, void *detail, void *userdata)
|
|
458
|
+
{
|
|
459
|
+
struct rbm_cParser *p = userdata;
|
|
460
|
+
if (!p->on_enter_block)
|
|
461
|
+
return 0;
|
|
462
|
+
VALUE args = rbm_callback_block_arguments (type, detail);
|
|
463
|
+
rb_proc_call (p->on_enter_block, args);
|
|
464
|
+
return 0;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
static int
|
|
468
|
+
rbm_callback_leave_block (MD_BLOCKTYPE type, void *detail, void *userdata)
|
|
469
|
+
{
|
|
470
|
+
struct rbm_cParser *p = userdata;
|
|
471
|
+
if (!p->on_leave_block)
|
|
472
|
+
return 0;
|
|
473
|
+
VALUE args = rbm_callback_block_arguments (type, detail);
|
|
474
|
+
rb_proc_call (p->on_leave_block, args);
|
|
475
|
+
return 0;
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
static VALUE
|
|
479
|
+
rbm_callback_span_arguments (MD_SPANTYPE type, void *detail)
|
|
480
|
+
{
|
|
481
|
+
VALUE arg;
|
|
482
|
+
switch (type)
|
|
483
|
+
{
|
|
484
|
+
case MD_SPAN_EM:
|
|
485
|
+
arg = rb_id2sym (rb_intern ("emphasis"));
|
|
486
|
+
break;
|
|
487
|
+
|
|
488
|
+
case MD_SPAN_STRONG:
|
|
489
|
+
arg = rb_id2sym (rb_intern ("strong"));
|
|
490
|
+
break;
|
|
491
|
+
|
|
492
|
+
case MD_SPAN_A:
|
|
493
|
+
{
|
|
494
|
+
arg = rb_class_new_instance (0, NULL, rbm_cAnchor);
|
|
495
|
+
|
|
496
|
+
MD_SPAN_A_DETAIL *d = detail;
|
|
497
|
+
|
|
498
|
+
VALUE href = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
499
|
+
RTYPEDDATA_DATA (href) = &d->href;
|
|
500
|
+
rb_iv_set (arg, "@href", href);
|
|
501
|
+
|
|
502
|
+
VALUE title = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
503
|
+
RTYPEDDATA_DATA (title) = &d->title;
|
|
504
|
+
rb_iv_set (arg, "@title", title);
|
|
505
|
+
|
|
506
|
+
VALUE autolink = d->is_autolink ? RUBY_Qtrue : RUBY_Qfalse;
|
|
507
|
+
rb_iv_set (arg, "@autolink", autolink);
|
|
508
|
+
break;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
case MD_SPAN_IMG:
|
|
512
|
+
{
|
|
513
|
+
arg = rb_class_new_instance (0, NULL, rbm_cImage);
|
|
514
|
+
|
|
515
|
+
MD_SPAN_IMG_DETAIL *d = detail;
|
|
516
|
+
|
|
517
|
+
VALUE src = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
518
|
+
RTYPEDDATA_DATA (src) = &d->src;
|
|
519
|
+
rb_iv_set (arg, "@src", src);
|
|
520
|
+
|
|
521
|
+
VALUE title = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
522
|
+
RTYPEDDATA_DATA (title) = &d->title;
|
|
523
|
+
rb_iv_set (arg, "@title", title);
|
|
524
|
+
break;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
case MD_SPAN_CODE:
|
|
528
|
+
arg = rb_id2sym (rb_intern ("code"));
|
|
529
|
+
break;
|
|
530
|
+
|
|
531
|
+
case MD_SPAN_DEL:
|
|
532
|
+
/* https://github.github.com/gfm/#strikethrough-extension- */
|
|
533
|
+
arg = rb_id2sym (rb_intern ("strikethrough"));
|
|
534
|
+
break;
|
|
535
|
+
|
|
536
|
+
case MD_SPAN_LATEXMATH:
|
|
537
|
+
arg = rb_id2sym (rb_intern ("inline_latex_math"));
|
|
538
|
+
break;
|
|
539
|
+
|
|
540
|
+
case MD_SPAN_LATEXMATH_DISPLAY:
|
|
541
|
+
arg = rb_id2sym (rb_intern ("display_latex_math"));
|
|
542
|
+
break;
|
|
543
|
+
|
|
544
|
+
case MD_SPAN_WIKILINK:
|
|
545
|
+
{
|
|
546
|
+
arg = rb_class_new_instance (0, NULL, rbm_cWikiLink);
|
|
547
|
+
|
|
548
|
+
MD_SPAN_WIKILINK_DETAIL *d = detail;
|
|
549
|
+
|
|
550
|
+
VALUE target = rb_class_new_instance (0, NULL, rbm_cStringAttribute);
|
|
551
|
+
RTYPEDDATA_DATA (target) = &d->target;
|
|
552
|
+
rb_iv_set (arg, "@target", target);
|
|
553
|
+
break;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
case MD_SPAN_U:
|
|
557
|
+
arg = rb_id2sym (rb_intern ("underline"));
|
|
558
|
+
break;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
VALUE args = rb_ary_new ();
|
|
562
|
+
rb_ary_push (args, arg);
|
|
563
|
+
return args;
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
static int
|
|
567
|
+
rbm_callback_enter_span (MD_SPANTYPE type, void *detail, void *userdata)
|
|
568
|
+
{
|
|
569
|
+
struct rbm_cParser *p = userdata;
|
|
570
|
+
if (!p->on_enter_span)
|
|
571
|
+
return 0;
|
|
572
|
+
VALUE args = rbm_callback_span_arguments (type, detail);
|
|
573
|
+
rb_proc_call (p->on_enter_span, args);
|
|
574
|
+
return 0;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
static int
|
|
578
|
+
rbm_callback_leave_span (MD_SPANTYPE type, void *detail, void *userdata)
|
|
579
|
+
{
|
|
580
|
+
struct rbm_cParser *p = userdata;
|
|
581
|
+
if (!p->on_leave_span)
|
|
582
|
+
return 0;
|
|
583
|
+
VALUE args = rbm_callback_span_arguments (type, detail);
|
|
584
|
+
rb_proc_call (p->on_leave_span, args);
|
|
585
|
+
return 0;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
static int
|
|
589
|
+
rbm_callback_text (MD_TEXTTYPE type, const MD_CHAR *text, MD_SIZE size,
|
|
590
|
+
void *userdata)
|
|
591
|
+
{
|
|
592
|
+
struct rbm_cParser *p = userdata;
|
|
593
|
+
if (!p->on_text)
|
|
594
|
+
return 0;
|
|
595
|
+
VALUE string = rb_str_new (text, size);
|
|
596
|
+
VALUE klass = rbm_get_text_class (type);
|
|
597
|
+
VALUE args = rb_ary_new ();
|
|
598
|
+
VALUE arg = rb_class_new_instance (0, NULL, klass);
|
|
599
|
+
rb_iv_set (arg, "@content", string);
|
|
600
|
+
rb_ary_push (args, arg);
|
|
601
|
+
rb_proc_call (p->on_text, args);
|
|
602
|
+
return 0;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
static VALUE
|
|
606
|
+
rbm_cParser_m_parse (VALUE self, VALUE source)
|
|
607
|
+
{
|
|
608
|
+
char *text = rb_string_value_ptr (&source);
|
|
609
|
+
long size = RSTRING_LEN (source);
|
|
610
|
+
struct rbm_cParser *p = RTYPEDDATA_DATA (self);
|
|
611
|
+
MD_PARSER parser = { .flags = p->flags,
|
|
612
|
+
.enter_block = rbm_callback_enter_block,
|
|
613
|
+
.leave_block = rbm_callback_leave_block,
|
|
614
|
+
.enter_span = rbm_callback_enter_span,
|
|
615
|
+
.leave_span = rbm_callback_leave_span,
|
|
616
|
+
.text = rbm_callback_text };
|
|
617
|
+
int result = md_parse (text, (MD_SIZE)size, &parser, p);
|
|
618
|
+
if (result == -1)
|
|
619
|
+
rb_raise (rbm_eError, "runtime error, such as memory fails");
|
|
620
|
+
if (result != 0)
|
|
621
|
+
rb_raise (rbm_eError, "parse failed");
|
|
622
|
+
return RUBY_Qnil;
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
struct rbm_markdown_to_html_userdata
|
|
626
|
+
{
|
|
627
|
+
VALUE block;
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
static void
|
|
631
|
+
rbm_markdown_to_html_callback (const MD_CHAR *input, MD_SIZE size,
|
|
632
|
+
void *userdata)
|
|
633
|
+
{
|
|
634
|
+
struct rbm_markdown_to_html_userdata *data = userdata;
|
|
635
|
+
VALUE text = rb_str_new (input, size);
|
|
636
|
+
VALUE args = rb_ary_new ();
|
|
637
|
+
rb_ary_push (args, text);
|
|
638
|
+
rb_proc_call (data->block, args);
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
static VALUE
|
|
642
|
+
rbm_m_markdown_to_html (int argc, VALUE *argv, VALUE self)
|
|
643
|
+
{
|
|
644
|
+
VALUE input, kwargs;
|
|
645
|
+
rb_scan_args (argc, argv, "1:", &input, &kwargs);
|
|
646
|
+
|
|
647
|
+
char *text = rb_string_value_ptr (&input);
|
|
648
|
+
long size = RSTRING_LEN (input);
|
|
649
|
+
|
|
650
|
+
ID kwargs_ids[] = { PARSER_FLAGS, rb_intern ("verbatim_entities"),
|
|
651
|
+
rb_intern ("skip_utf8_bom"), rb_intern ("xhtml") };
|
|
652
|
+
VALUE kwargs_vals[sizeof (kwargs_ids) / sizeof (kwargs_ids[0])] = { 0 };
|
|
653
|
+
rb_get_kwargs (kwargs, kwargs_ids, 0,
|
|
654
|
+
sizeof (kwargs_ids) / sizeof (kwargs_ids[0]), kwargs_vals);
|
|
655
|
+
|
|
656
|
+
unsigned pflags = rbm_get_parser_flags (kwargs_vals);
|
|
657
|
+
|
|
658
|
+
unsigned rflags = 0;
|
|
659
|
+
|
|
660
|
+
if (!RB_UNDEF_P (kwargs_vals[19]) || RB_TEST (kwargs_vals[19]))
|
|
661
|
+
rflags |= MD_HTML_FLAG_VERBATIM_ENTITIES;
|
|
662
|
+
|
|
663
|
+
if (!RB_UNDEF_P (kwargs_vals[20]) || RB_TEST (kwargs_vals[20]))
|
|
664
|
+
rflags |= MD_HTML_FLAG_SKIP_UTF8_BOM;
|
|
665
|
+
|
|
666
|
+
if (!RB_UNDEF_P (kwargs_vals[21]) || RB_TEST (kwargs_vals[21]))
|
|
667
|
+
rflags |= MD_HTML_FLAG_XHTML;
|
|
668
|
+
|
|
669
|
+
struct rbm_markdown_to_html_userdata *userdata
|
|
670
|
+
= ruby_xmalloc (sizeof (struct rbm_markdown_to_html_userdata));
|
|
671
|
+
*userdata
|
|
672
|
+
= (struct rbm_markdown_to_html_userdata){ .block = rb_block_proc () };
|
|
673
|
+
if (md_html (text, (MD_SIZE)size, rbm_markdown_to_html_callback, userdata,
|
|
674
|
+
pflags, rflags)
|
|
675
|
+
!= 0)
|
|
676
|
+
{
|
|
677
|
+
ruby_xfree (userdata);
|
|
678
|
+
rb_raise (rbm_eError, "conversion fails");
|
|
679
|
+
}
|
|
680
|
+
ruby_xfree (userdata);
|
|
681
|
+
return RUBY_Qnil;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
RUBY_FUNC_EXPORTED void
|
|
685
|
+
Init_md4c (void)
|
|
686
|
+
{
|
|
687
|
+
rb_mMD4C = rb_define_module ("MD4C");
|
|
688
|
+
|
|
689
|
+
rbm_cStringAttribute
|
|
690
|
+
= rb_define_class_under (rb_mMD4C, "StringAttribute", rb_cObject);
|
|
691
|
+
rb_define_alloc_func (rbm_cStringAttribute, rbm_cStringAttribute_alloc);
|
|
692
|
+
rb_define_method (rbm_cStringAttribute, "each", rbm_cStringAttribute_m_each,
|
|
693
|
+
0);
|
|
694
|
+
|
|
695
|
+
rbm_cUnorderedList
|
|
696
|
+
= rb_define_class_under (rb_mMD4C, "UnorderedList", rb_cObject);
|
|
697
|
+
rbm_cOrderedList
|
|
698
|
+
= rb_define_class_under (rb_mMD4C, "OrderedList", rb_cObject);
|
|
699
|
+
rbm_cListItem = rb_define_class_under (rb_mMD4C, "ListItem", rb_cObject);
|
|
700
|
+
rbm_cHeading = rb_define_class_under (rb_mMD4C, "Heading", rb_cObject);
|
|
701
|
+
rbm_cCodeBlock = rb_define_class_under (rb_mMD4C, "CodeBlock", rb_cObject);
|
|
702
|
+
rbm_cTable = rb_define_class_under (rb_mMD4C, "Table", rb_cObject);
|
|
703
|
+
rbm_cTableHeader
|
|
704
|
+
= rb_define_class_under (rb_mMD4C, "TableHeader", rb_cObject);
|
|
705
|
+
rbm_cTableData = rb_define_class_under (rb_mMD4C, "TableData", rb_cObject);
|
|
706
|
+
|
|
707
|
+
rbm_cAnchor = rb_define_class_under (rb_mMD4C, "Anchor", rb_cObject);
|
|
708
|
+
rbm_cImage = rb_define_class_under (rb_mMD4C, "Image", rb_cObject);
|
|
709
|
+
rbm_cWikiLink = rb_define_class_under (rb_mMD4C, "WikiLink", rb_cObject);
|
|
710
|
+
|
|
711
|
+
rbm_cNormalText = rb_define_class_under (rb_mMD4C, "NormalText", rb_cObject);
|
|
712
|
+
rbm_cNullChar = rb_define_class_under (rb_mMD4C, "NullChar", rb_cObject);
|
|
713
|
+
rbm_cHardBreak = rb_define_class_under (rb_mMD4C, "HardBreak", rb_cObject);
|
|
714
|
+
rbm_cSoftBreak = rb_define_class_under (rb_mMD4C, "SoftBreak", rb_cObject);
|
|
715
|
+
rbm_cEntity = rb_define_class_under (rb_mMD4C, "Entity", rb_cObject);
|
|
716
|
+
rbm_cCodeText = rb_define_class_under (rb_mMD4C, "CodeText", rb_cObject);
|
|
717
|
+
rbm_cHTMLText = rb_define_class_under (rb_mMD4C, "HTMLText", rb_cObject);
|
|
718
|
+
rbm_cLaTeXMathText
|
|
719
|
+
= rb_define_class_under (rb_mMD4C, "LaTeXMathText", rb_cObject);
|
|
720
|
+
|
|
721
|
+
rbm_cParser = rb_define_class_under (rb_mMD4C, "Parser", rb_cObject);
|
|
722
|
+
rb_define_alloc_func (rbm_cParser, rbm_cParser_alloc);
|
|
723
|
+
rb_define_method (rbm_cParser, "initialize", rbm_cParser_m_initialize, -1);
|
|
724
|
+
|
|
725
|
+
rb_define_method (rbm_cParser, "on_enter_block",
|
|
726
|
+
rbm_cParser_m_on_enter_block, 0);
|
|
727
|
+
rb_define_method (rbm_cParser, "on_leave_block",
|
|
728
|
+
rbm_cParser_m_on_leave_block, 0);
|
|
729
|
+
rb_define_method (rbm_cParser, "on_enter_span", rbm_cParser_m_on_enter_span,
|
|
730
|
+
0);
|
|
731
|
+
rb_define_method (rbm_cParser, "on_leave_span", rbm_cParser_m_on_leave_span,
|
|
732
|
+
0);
|
|
733
|
+
rb_define_method (rbm_cParser, "on_text", rbm_cParser_m_on_text, 0);
|
|
734
|
+
|
|
735
|
+
rb_define_method (rbm_cParser, "parse", rbm_cParser_m_parse, 1);
|
|
736
|
+
|
|
737
|
+
rb_define_module_function (rb_mMD4C, "markdown_to_html",
|
|
738
|
+
rbm_m_markdown_to_html, -1);
|
|
739
|
+
|
|
740
|
+
rbm_eError = rb_define_class_under (rb_mMD4C, "Error", rb_eStandardError);
|
|
741
|
+
}
|