markly 0.1.0
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/bin/markly +94 -0
- data/ext/markly/arena.c +103 -0
- data/ext/markly/autolink.c +425 -0
- data/ext/markly/autolink.h +8 -0
- data/ext/markly/blocks.c +1585 -0
- data/ext/markly/buffer.c +278 -0
- data/ext/markly/buffer.h +116 -0
- data/ext/markly/case_fold_switch.inc +4327 -0
- data/ext/markly/chunk.h +135 -0
- data/ext/markly/cmark-gfm-core-extensions.h +54 -0
- data/ext/markly/cmark-gfm-extension_api.h +736 -0
- data/ext/markly/cmark-gfm-extensions_export.h +42 -0
- data/ext/markly/cmark-gfm.h +817 -0
- data/ext/markly/cmark-gfm_export.h +42 -0
- data/ext/markly/cmark-gfm_version.h +7 -0
- data/ext/markly/cmark.c +55 -0
- data/ext/markly/cmark_ctype.c +44 -0
- data/ext/markly/cmark_ctype.h +33 -0
- data/ext/markly/commonmark.c +519 -0
- data/ext/markly/config.h +76 -0
- data/ext/markly/core-extensions.c +27 -0
- data/ext/markly/entities.inc +2138 -0
- data/ext/markly/ext_scanners.c +1159 -0
- data/ext/markly/ext_scanners.h +24 -0
- data/ext/markly/extconf.rb +7 -0
- data/ext/markly/footnotes.c +40 -0
- data/ext/markly/footnotes.h +25 -0
- data/ext/markly/houdini.h +57 -0
- data/ext/markly/houdini_href_e.c +100 -0
- data/ext/markly/houdini_html_e.c +66 -0
- data/ext/markly/houdini_html_u.c +149 -0
- data/ext/markly/html.c +465 -0
- data/ext/markly/html.h +27 -0
- data/ext/markly/inlines.c +1633 -0
- data/ext/markly/inlines.h +29 -0
- data/ext/markly/iterator.c +159 -0
- data/ext/markly/iterator.h +26 -0
- data/ext/markly/latex.c +466 -0
- data/ext/markly/linked_list.c +37 -0
- data/ext/markly/man.c +278 -0
- data/ext/markly/map.c +122 -0
- data/ext/markly/map.h +41 -0
- data/ext/markly/markly.c +1226 -0
- data/ext/markly/markly.h +16 -0
- data/ext/markly/node.c +979 -0
- data/ext/markly/node.h +118 -0
- data/ext/markly/parser.h +58 -0
- data/ext/markly/plaintext.c +235 -0
- data/ext/markly/plugin.c +36 -0
- data/ext/markly/plugin.h +34 -0
- data/ext/markly/references.c +42 -0
- data/ext/markly/references.h +26 -0
- data/ext/markly/registry.c +63 -0
- data/ext/markly/registry.h +24 -0
- data/ext/markly/render.c +205 -0
- data/ext/markly/render.h +62 -0
- data/ext/markly/scanners.c +20382 -0
- data/ext/markly/scanners.h +62 -0
- data/ext/markly/scanners.re +326 -0
- data/ext/markly/strikethrough.c +167 -0
- data/ext/markly/strikethrough.h +9 -0
- data/ext/markly/syntax_extension.c +149 -0
- data/ext/markly/syntax_extension.h +34 -0
- data/ext/markly/table.c +803 -0
- data/ext/markly/table.h +12 -0
- data/ext/markly/tagfilter.c +60 -0
- data/ext/markly/tagfilter.h +8 -0
- data/ext/markly/tasklist.c +156 -0
- data/ext/markly/tasklist.h +8 -0
- data/ext/markly/utf8.c +317 -0
- data/ext/markly/utf8.h +35 -0
- data/ext/markly/xml.c +181 -0
- data/lib/markly.rb +43 -0
- data/lib/markly/flags.rb +37 -0
- data/lib/markly/markly.so +0 -0
- data/lib/markly/node.rb +70 -0
- data/lib/markly/node/inspect.rb +59 -0
- data/lib/markly/renderer.rb +133 -0
- data/lib/markly/renderer/html_renderer.rb +252 -0
- data/lib/markly/version.rb +5 -0
- metadata +211 -0
data/ext/markly/utf8.h
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
#ifndef CMARK_UTF8_H
|
2
|
+
#define CMARK_UTF8_H
|
3
|
+
|
4
|
+
#include <stdint.h>
|
5
|
+
#include "buffer.h"
|
6
|
+
|
7
|
+
#ifdef __cplusplus
|
8
|
+
extern "C" {
|
9
|
+
#endif
|
10
|
+
|
11
|
+
CMARK_GFM_EXPORT
|
12
|
+
void cmark_utf8proc_case_fold(cmark_strbuf *dest, const uint8_t *str,
|
13
|
+
bufsize_t len);
|
14
|
+
|
15
|
+
CMARK_GFM_EXPORT
|
16
|
+
void cmark_utf8proc_encode_char(int32_t uc, cmark_strbuf *buf);
|
17
|
+
|
18
|
+
CMARK_GFM_EXPORT
|
19
|
+
int cmark_utf8proc_iterate(const uint8_t *str, bufsize_t str_len, int32_t *dst);
|
20
|
+
|
21
|
+
CMARK_GFM_EXPORT
|
22
|
+
void cmark_utf8proc_check(cmark_strbuf *dest, const uint8_t *line,
|
23
|
+
bufsize_t size);
|
24
|
+
|
25
|
+
CMARK_GFM_EXPORT
|
26
|
+
int cmark_utf8proc_is_space(int32_t uc);
|
27
|
+
|
28
|
+
CMARK_GFM_EXPORT
|
29
|
+
int cmark_utf8proc_is_punctuation(int32_t uc);
|
30
|
+
|
31
|
+
#ifdef __cplusplus
|
32
|
+
}
|
33
|
+
#endif
|
34
|
+
|
35
|
+
#endif
|
data/ext/markly/xml.c
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
#include <stdlib.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <string.h>
|
4
|
+
#include <assert.h>
|
5
|
+
|
6
|
+
#include "config.h"
|
7
|
+
#include "cmark-gfm.h"
|
8
|
+
#include "node.h"
|
9
|
+
#include "buffer.h"
|
10
|
+
#include "houdini.h"
|
11
|
+
#include "syntax_extension.h"
|
12
|
+
|
13
|
+
#define BUFFER_SIZE 100
|
14
|
+
|
15
|
+
// Functions to convert cmark_nodes to XML strings.
|
16
|
+
|
17
|
+
static void escape_xml(cmark_strbuf *dest, const unsigned char *source,
|
18
|
+
bufsize_t length) {
|
19
|
+
houdini_escape_html0(dest, source, length, 0);
|
20
|
+
}
|
21
|
+
|
22
|
+
struct render_state {
|
23
|
+
cmark_strbuf *xml;
|
24
|
+
int indent;
|
25
|
+
};
|
26
|
+
|
27
|
+
static CMARK_INLINE void indent(struct render_state *state) {
|
28
|
+
int i;
|
29
|
+
for (i = 0; i < state->indent; i++) {
|
30
|
+
cmark_strbuf_putc(state->xml, ' ');
|
31
|
+
}
|
32
|
+
}
|
33
|
+
|
34
|
+
static int S_render_node(cmark_node *node, cmark_event_type ev_type,
|
35
|
+
struct render_state *state, int options) {
|
36
|
+
cmark_strbuf *xml = state->xml;
|
37
|
+
bool literal = false;
|
38
|
+
cmark_delim_type delim;
|
39
|
+
bool entering = (ev_type == CMARK_EVENT_ENTER);
|
40
|
+
char buffer[BUFFER_SIZE];
|
41
|
+
|
42
|
+
if (entering) {
|
43
|
+
indent(state);
|
44
|
+
cmark_strbuf_putc(xml, '<');
|
45
|
+
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
46
|
+
|
47
|
+
if (options & CMARK_OPT_SOURCEPOS && node->start_line != 0) {
|
48
|
+
snprintf(buffer, BUFFER_SIZE, " sourcepos=\"%d:%d-%d:%d\"",
|
49
|
+
node->start_line, node->start_column, node->end_line,
|
50
|
+
node->end_column);
|
51
|
+
cmark_strbuf_puts(xml, buffer);
|
52
|
+
}
|
53
|
+
|
54
|
+
if (node->extension && node->extension->xml_attr_func) {
|
55
|
+
const char* r = node->extension->xml_attr_func(node->extension, node);
|
56
|
+
if (r != NULL)
|
57
|
+
cmark_strbuf_puts(xml, r);
|
58
|
+
}
|
59
|
+
|
60
|
+
literal = false;
|
61
|
+
|
62
|
+
switch (node->type) {
|
63
|
+
case CMARK_NODE_DOCUMENT:
|
64
|
+
cmark_strbuf_puts(xml, " xmlns=\"http://commonmark.org/xml/1.0\"");
|
65
|
+
break;
|
66
|
+
case CMARK_NODE_TEXT:
|
67
|
+
case CMARK_NODE_CODE:
|
68
|
+
case CMARK_NODE_HTML_BLOCK:
|
69
|
+
case CMARK_NODE_HTML_INLINE:
|
70
|
+
cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
|
71
|
+
escape_xml(xml, node->as.literal.data, node->as.literal.len);
|
72
|
+
cmark_strbuf_puts(xml, "</");
|
73
|
+
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
74
|
+
literal = true;
|
75
|
+
break;
|
76
|
+
case CMARK_NODE_LIST:
|
77
|
+
switch (cmark_node_get_list_type(node)) {
|
78
|
+
case CMARK_ORDERED_LIST:
|
79
|
+
cmark_strbuf_puts(xml, " type=\"ordered\"");
|
80
|
+
snprintf(buffer, BUFFER_SIZE, " start=\"%d\"",
|
81
|
+
cmark_node_get_list_start(node));
|
82
|
+
cmark_strbuf_puts(xml, buffer);
|
83
|
+
delim = cmark_node_get_list_delim(node);
|
84
|
+
if (delim == CMARK_PAREN_DELIM) {
|
85
|
+
cmark_strbuf_puts(xml, " delim=\"paren\"");
|
86
|
+
} else if (delim == CMARK_PERIOD_DELIM) {
|
87
|
+
cmark_strbuf_puts(xml, " delim=\"period\"");
|
88
|
+
}
|
89
|
+
break;
|
90
|
+
case CMARK_BULLET_LIST:
|
91
|
+
cmark_strbuf_puts(xml, " type=\"bullet\"");
|
92
|
+
break;
|
93
|
+
default:
|
94
|
+
break;
|
95
|
+
}
|
96
|
+
snprintf(buffer, BUFFER_SIZE, " tight=\"%s\"",
|
97
|
+
(cmark_node_get_list_tight(node) ? "true" : "false"));
|
98
|
+
cmark_strbuf_puts(xml, buffer);
|
99
|
+
break;
|
100
|
+
case CMARK_NODE_HEADING:
|
101
|
+
snprintf(buffer, BUFFER_SIZE, " level=\"%d\"", node->as.heading.level);
|
102
|
+
cmark_strbuf_puts(xml, buffer);
|
103
|
+
break;
|
104
|
+
case CMARK_NODE_CODE_BLOCK:
|
105
|
+
if (node->as.code.info.len > 0) {
|
106
|
+
cmark_strbuf_puts(xml, " info=\"");
|
107
|
+
escape_xml(xml, node->as.code.info.data, node->as.code.info.len);
|
108
|
+
cmark_strbuf_putc(xml, '"');
|
109
|
+
}
|
110
|
+
cmark_strbuf_puts(xml, " xml:space=\"preserve\">");
|
111
|
+
escape_xml(xml, node->as.code.literal.data, node->as.code.literal.len);
|
112
|
+
cmark_strbuf_puts(xml, "</");
|
113
|
+
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
114
|
+
literal = true;
|
115
|
+
break;
|
116
|
+
case CMARK_NODE_CUSTOM_BLOCK:
|
117
|
+
case CMARK_NODE_CUSTOM_INLINE:
|
118
|
+
cmark_strbuf_puts(xml, " on_enter=\"");
|
119
|
+
escape_xml(xml, node->as.custom.on_enter.data,
|
120
|
+
node->as.custom.on_enter.len);
|
121
|
+
cmark_strbuf_putc(xml, '"');
|
122
|
+
cmark_strbuf_puts(xml, " on_exit=\"");
|
123
|
+
escape_xml(xml, node->as.custom.on_exit.data,
|
124
|
+
node->as.custom.on_exit.len);
|
125
|
+
cmark_strbuf_putc(xml, '"');
|
126
|
+
break;
|
127
|
+
case CMARK_NODE_LINK:
|
128
|
+
case CMARK_NODE_IMAGE:
|
129
|
+
cmark_strbuf_puts(xml, " destination=\"");
|
130
|
+
escape_xml(xml, node->as.link.url.data, node->as.link.url.len);
|
131
|
+
cmark_strbuf_putc(xml, '"');
|
132
|
+
cmark_strbuf_puts(xml, " title=\"");
|
133
|
+
escape_xml(xml, node->as.link.title.data, node->as.link.title.len);
|
134
|
+
cmark_strbuf_putc(xml, '"');
|
135
|
+
break;
|
136
|
+
default:
|
137
|
+
break;
|
138
|
+
}
|
139
|
+
if (node->first_child) {
|
140
|
+
state->indent += 2;
|
141
|
+
} else if (!literal) {
|
142
|
+
cmark_strbuf_puts(xml, " /");
|
143
|
+
}
|
144
|
+
cmark_strbuf_puts(xml, ">\n");
|
145
|
+
|
146
|
+
} else if (node->first_child) {
|
147
|
+
state->indent -= 2;
|
148
|
+
indent(state);
|
149
|
+
cmark_strbuf_puts(xml, "</");
|
150
|
+
cmark_strbuf_puts(xml, cmark_node_get_type_string(node));
|
151
|
+
cmark_strbuf_puts(xml, ">\n");
|
152
|
+
}
|
153
|
+
|
154
|
+
return 1;
|
155
|
+
}
|
156
|
+
|
157
|
+
char *cmark_render_xml(cmark_node *root, int options) {
|
158
|
+
return cmark_render_xml_with_mem(root, options, cmark_node_mem(root));
|
159
|
+
}
|
160
|
+
|
161
|
+
char *cmark_render_xml_with_mem(cmark_node *root, int options, cmark_mem *mem) {
|
162
|
+
char *result;
|
163
|
+
cmark_strbuf xml = CMARK_BUF_INIT(mem);
|
164
|
+
cmark_event_type ev_type;
|
165
|
+
cmark_node *cur;
|
166
|
+
struct render_state state = {&xml, 0};
|
167
|
+
|
168
|
+
cmark_iter *iter = cmark_iter_new(root);
|
169
|
+
|
170
|
+
cmark_strbuf_puts(state.xml, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
|
171
|
+
cmark_strbuf_puts(state.xml,
|
172
|
+
"<!DOCTYPE document SYSTEM \"CommonMark.dtd\">\n");
|
173
|
+
while ((ev_type = cmark_iter_next(iter)) != CMARK_EVENT_DONE) {
|
174
|
+
cur = cmark_iter_get_node(iter);
|
175
|
+
S_render_node(cur, ev_type, &state, options);
|
176
|
+
}
|
177
|
+
result = (char *)cmark_strbuf_detach(&xml);
|
178
|
+
|
179
|
+
cmark_iter_free(iter);
|
180
|
+
return result;
|
181
|
+
}
|
data/lib/markly.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
# The compiled library.
|
5
|
+
require_relative 'markly/markly'
|
6
|
+
|
7
|
+
require_relative 'markly/flags'
|
8
|
+
require_relative 'markly/node'
|
9
|
+
require_relative 'markly/renderer'
|
10
|
+
require_relative 'markly/renderer/html_renderer'
|
11
|
+
require_relative 'markly/version'
|
12
|
+
|
13
|
+
module Markly
|
14
|
+
# Public: Parses a Markdown string into a `document` node.
|
15
|
+
#
|
16
|
+
# string - {String} to be parsed
|
17
|
+
# option - A {Symbol} or {Array of Symbol}s indicating the parse options
|
18
|
+
# extensions - An {Array of Symbol}s indicating the extensions to use
|
19
|
+
#
|
20
|
+
# Returns the `parser` node.
|
21
|
+
def self.parse(text, flags: DEFAULT, extensions: nil)
|
22
|
+
parser = Parser.new(flags)
|
23
|
+
|
24
|
+
extensions&.each do |extension|
|
25
|
+
parser.enable(extension)
|
26
|
+
end
|
27
|
+
|
28
|
+
return parser.parse(text.encode(Encoding::UTF_8))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Public: Parses a Markdown string into an HTML string.
|
32
|
+
#
|
33
|
+
# text - A {String} of text
|
34
|
+
# option - Either a {Symbol} or {Array of Symbol}s indicating the render options
|
35
|
+
# extensions - An {Array of Symbol}s indicating the extensions to use
|
36
|
+
#
|
37
|
+
# Returns a {String} of converted HTML.
|
38
|
+
def self.render_html(text, flags: DEFAULT, parse_flags: flags, render_flags: flags, extensions: [])
|
39
|
+
root = self.parse(text, flags: parse_flags, extensions: extensions)
|
40
|
+
|
41
|
+
return root.to_html(flags: render_flags, extensions: extensions)
|
42
|
+
end
|
43
|
+
end
|
data/lib/markly/flags.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Markly
|
4
|
+
DEFAULT = 0
|
5
|
+
VALIDATE_UTF8 = 1 << 9
|
6
|
+
SMART = 1 << 10
|
7
|
+
LIBERAL_HTML_TAG = 1 << 12
|
8
|
+
FOOTNOTES = 1 << 13
|
9
|
+
STRIKETHROUGH_DOUBLE_TILDE = 1 << 14
|
10
|
+
UNSAFE = 1 << 17
|
11
|
+
|
12
|
+
PARSE_FLAGS = {
|
13
|
+
validate_utf8: VALIDATE_UTF8,
|
14
|
+
smart_quotes: SMART,
|
15
|
+
liberal_html_tags: LIBERAL_HTML_TAG,
|
16
|
+
footnotes: FOOTNOTES,
|
17
|
+
strikethrough_double_tilde: STRIKETHROUGH_DOUBLE_TILDE,
|
18
|
+
unsafe: UNSAFE,
|
19
|
+
}
|
20
|
+
|
21
|
+
SOURCE_POSITION = 1 << 1
|
22
|
+
HARD_BREAKS = 1 << 2
|
23
|
+
NO_BREAKS = 1 << 4
|
24
|
+
GITHUB_PRE_LANG = 1 << 11
|
25
|
+
TABLE_PREFER_STYLE_ATTRIBUTES = 1 << 15
|
26
|
+
FULL_INFO_STRING = 1 << 16
|
27
|
+
|
28
|
+
RENDER_FLAGS = {
|
29
|
+
source_position: SOURCE_POSITION,
|
30
|
+
hard_breaks: HARD_BREAKS,
|
31
|
+
no_breaks: NO_BREAKS,
|
32
|
+
pre_lang: GITHUB_PRE_LANG,
|
33
|
+
table_prefer_style_attributes: TABLE_PREFER_STYLE_ATTRIBUTES,
|
34
|
+
full_info_string: FULL_INFO_STRING,
|
35
|
+
unsafe: UNSAFE,
|
36
|
+
}
|
37
|
+
end
|
Binary file
|
data/lib/markly/node.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'node/inspect'
|
4
|
+
|
5
|
+
module Markly
|
6
|
+
class Node
|
7
|
+
include Enumerable
|
8
|
+
include Inspect
|
9
|
+
|
10
|
+
# Public: An iterator that "walks the tree," descending into children recursively.
|
11
|
+
#
|
12
|
+
# blk - A {Proc} representing the action to take for each child
|
13
|
+
def walk(&block)
|
14
|
+
return enum_for(:walk) unless block_given?
|
15
|
+
|
16
|
+
yield self
|
17
|
+
each do |child|
|
18
|
+
child.walk(&block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Public: Convert the node to an HTML string.
|
23
|
+
#
|
24
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
25
|
+
# extensions - An {Array of Symbol}s indicating the extensions to use
|
26
|
+
#
|
27
|
+
# Returns a {String}.
|
28
|
+
def to_html(flags: DEFAULT, extensions: [])
|
29
|
+
_render_html(flags, extensions).force_encoding('utf-8')
|
30
|
+
end
|
31
|
+
|
32
|
+
# Public: Convert the node to a CommonMark string.
|
33
|
+
#
|
34
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
35
|
+
# width - Column to wrap the output at
|
36
|
+
#
|
37
|
+
# Returns a {String}.
|
38
|
+
def to_commonmark(flags: DEFAULT, width: 120)
|
39
|
+
_render_commonmark(flags, width).force_encoding('utf-8')
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Convert the node to a plain text string.
|
43
|
+
#
|
44
|
+
# options - A {Symbol} or {Array of Symbol}s indicating the render options
|
45
|
+
# width - Column to wrap the output at
|
46
|
+
#
|
47
|
+
# Returns a {String}.
|
48
|
+
def to_plaintext(flags: DEFAULT, width: 120)
|
49
|
+
_render_plaintext(flags, width).force_encoding('utf-8')
|
50
|
+
end
|
51
|
+
|
52
|
+
# Public: Iterate over the children (if any) of the current pointer.
|
53
|
+
def each
|
54
|
+
return enum_for(:each) unless block_given?
|
55
|
+
|
56
|
+
child = first_child
|
57
|
+
while child
|
58
|
+
nextchild = child.next
|
59
|
+
yield child
|
60
|
+
child = nextchild
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Deprecated: Please use `each` instead
|
65
|
+
def each_child(&block)
|
66
|
+
warn '[DEPRECATION] `each_child` is deprecated. Please use `each` instead.'
|
67
|
+
each(&block)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
|
5
|
+
module Markly
|
6
|
+
class Node
|
7
|
+
module Inspect
|
8
|
+
PP_INDENT_SIZE = 2
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
PP.pp(self, +'', Float::INFINITY)
|
12
|
+
end
|
13
|
+
|
14
|
+
# @param printer [PrettyPrint] pp
|
15
|
+
def pretty_print(printer)
|
16
|
+
printer.group(PP_INDENT_SIZE, "#<#{self.class}(#{type}):", '>') do
|
17
|
+
printer.breakable
|
18
|
+
|
19
|
+
attrs = %i[
|
20
|
+
source_position
|
21
|
+
string_content
|
22
|
+
url
|
23
|
+
title
|
24
|
+
header_level
|
25
|
+
list_type
|
26
|
+
list_start
|
27
|
+
list_tight
|
28
|
+
fence_info
|
29
|
+
].map do |name|
|
30
|
+
begin
|
31
|
+
[name, __send__(name)]
|
32
|
+
rescue Error
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end.compact
|
36
|
+
|
37
|
+
printer.seplist(attrs) do |name, value|
|
38
|
+
printer.text "#{name}="
|
39
|
+
printer.pp value
|
40
|
+
end
|
41
|
+
|
42
|
+
if first_child
|
43
|
+
printer.breakable
|
44
|
+
printer.group(PP_INDENT_SIZE) do
|
45
|
+
children = []
|
46
|
+
node = first_child
|
47
|
+
while node
|
48
|
+
children << node
|
49
|
+
node = node.next
|
50
|
+
end
|
51
|
+
printer.text 'children='
|
52
|
+
printer.pp children
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'set'
|
4
|
+
require 'stringio'
|
5
|
+
|
6
|
+
module Markly
|
7
|
+
class Renderer
|
8
|
+
attr_accessor :in_tight, :warnings, :in_plain
|
9
|
+
def initialize(flags: DEFAULT, extensions: [])
|
10
|
+
@flags = flags
|
11
|
+
@stream = StringIO.new(+'')
|
12
|
+
@need_blocksep = false
|
13
|
+
@warnings = Set.new
|
14
|
+
@in_tight = false
|
15
|
+
@in_plain = false
|
16
|
+
@tagfilter = extensions.include?(:tagfilter)
|
17
|
+
end
|
18
|
+
|
19
|
+
def out(*args)
|
20
|
+
args.each do |arg|
|
21
|
+
if arg == :children
|
22
|
+
@node.each { |child| out(child) }
|
23
|
+
elsif arg.is_a?(Array)
|
24
|
+
arg.each { |x| render(x) }
|
25
|
+
elsif arg.is_a?(Node)
|
26
|
+
render(arg)
|
27
|
+
else
|
28
|
+
@stream.write(arg)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def render(node)
|
34
|
+
@node = node
|
35
|
+
if node.type == :document
|
36
|
+
document(node)
|
37
|
+
@stream.string
|
38
|
+
elsif @in_plain && node.type != :text && node.type != :softbreak
|
39
|
+
node.each { |child| render(child) }
|
40
|
+
else
|
41
|
+
begin
|
42
|
+
send(node.type, node)
|
43
|
+
rescue NoMethodError => e
|
44
|
+
@warnings.add("WARNING: #{node.type} not implemented.")
|
45
|
+
raise e
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def document(_node)
|
51
|
+
out(:children)
|
52
|
+
end
|
53
|
+
|
54
|
+
def code_block(node)
|
55
|
+
code_block(node)
|
56
|
+
end
|
57
|
+
|
58
|
+
def reference_def(_node); end
|
59
|
+
|
60
|
+
def cr
|
61
|
+
return if @stream.string.empty? || @stream.string[-1] == "\n"
|
62
|
+
|
63
|
+
out("\n")
|
64
|
+
end
|
65
|
+
|
66
|
+
def blocksep
|
67
|
+
out("\n")
|
68
|
+
end
|
69
|
+
|
70
|
+
def containersep
|
71
|
+
cr unless @in_tight
|
72
|
+
end
|
73
|
+
|
74
|
+
def block
|
75
|
+
cr
|
76
|
+
yield
|
77
|
+
cr
|
78
|
+
end
|
79
|
+
|
80
|
+
def container(starter, ender)
|
81
|
+
out(starter)
|
82
|
+
yield
|
83
|
+
out(ender)
|
84
|
+
end
|
85
|
+
|
86
|
+
def plain
|
87
|
+
old_in_plain = @in_plain
|
88
|
+
@in_plain = true
|
89
|
+
yield
|
90
|
+
@in_plain = old_in_plain
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def escape_href(str)
|
96
|
+
@node.html_escape_href(str)
|
97
|
+
end
|
98
|
+
|
99
|
+
def escape_html(str)
|
100
|
+
@node.html_escape_html(str)
|
101
|
+
end
|
102
|
+
|
103
|
+
def tagfilter(str)
|
104
|
+
if @tagfilter
|
105
|
+
str.gsub(
|
106
|
+
%r{
|
107
|
+
<
|
108
|
+
(
|
109
|
+
title|textarea|style|xmp|iframe|
|
110
|
+
noembed|noframes|script|plaintext
|
111
|
+
)
|
112
|
+
(?=\s|>|/>)
|
113
|
+
}xi,
|
114
|
+
'<\1'
|
115
|
+
)
|
116
|
+
else
|
117
|
+
str
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def source_position(node)
|
122
|
+
return '' unless flag_enabled?(SOURCE_POSITION)
|
123
|
+
|
124
|
+
s = node.source_position
|
125
|
+
" data-sourcepos=\"#{s[:start_line]}:#{s[:start_column]}-" \
|
126
|
+
"#{s[:end_line]}:#{s[:end_column]}\""
|
127
|
+
end
|
128
|
+
|
129
|
+
def flag_enabled?(flag)
|
130
|
+
(@flags & flag) != 0
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|