maruku 0.6.1 → 0.7.0.beta1
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
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/MIT-LICENSE.txt +20 -0
- data/bin/maruku +153 -152
- data/bin/marutex +2 -29
- data/data/entities.xml +261 -0
- data/docs/math.md +14 -18
- data/lib/maruku.rb +65 -77
- data/lib/maruku/attributes.rb +109 -214
- data/lib/maruku/defaults.rb +45 -67
- data/lib/maruku/document.rb +43 -0
- data/lib/maruku/element.rb +112 -0
- data/lib/maruku/errors.rb +71 -0
- data/lib/maruku/ext/div.rb +105 -113
- data/lib/maruku/ext/fenced_code.rb +97 -0
- data/lib/maruku/ext/math.rb +22 -26
- data/lib/maruku/ext/math/elements.rb +20 -26
- data/lib/maruku/ext/math/mathml_engines/blahtex.rb +92 -104
- data/lib/maruku/ext/math/mathml_engines/itex2mml.rb +33 -26
- data/lib/maruku/ext/math/mathml_engines/none.rb +11 -19
- data/lib/maruku/ext/math/mathml_engines/ritex.rb +2 -4
- data/lib/maruku/ext/math/parsing.rb +107 -113
- data/lib/maruku/ext/math/to_html.rb +184 -187
- data/lib/maruku/ext/math/to_latex.rb +30 -21
- data/lib/maruku/helpers.rb +158 -257
- data/lib/maruku/html.rb +254 -0
- data/lib/maruku/input/charsource.rb +272 -319
- data/lib/maruku/input/extensions.rb +62 -63
- data/lib/maruku/input/html_helper.rb +220 -189
- data/lib/maruku/input/linesource.rb +90 -110
- data/lib/maruku/input/mdline.rb +129 -0
- data/lib/maruku/input/parse_block.rb +618 -612
- data/lib/maruku/input/parse_doc.rb +145 -215
- data/lib/maruku/input/parse_span.rb +658 -0
- data/lib/maruku/input/rubypants.rb +200 -128
- data/lib/maruku/inspect_element.rb +60 -0
- data/lib/maruku/maruku.rb +10 -31
- data/lib/maruku/output/entity_table.rb +33 -0
- data/lib/maruku/output/s5/fancy.rb +462 -462
- data/lib/maruku/output/s5/to_s5.rb +115 -135
- data/lib/maruku/output/to_html.rb +898 -983
- data/lib/maruku/output/to_latex.rb +561 -560
- data/lib/maruku/output/to_markdown.rb +207 -162
- data/lib/maruku/output/to_s.rb +11 -52
- data/lib/maruku/string_utils.rb +129 -179
- data/lib/maruku/toc.rb +185 -196
- data/lib/maruku/version.rb +33 -38
- data/spec/block_docs/abbrev.md +776 -0
- data/{tests/unittest → spec/block_docs}/abbreviations.md +11 -20
- data/{tests/unittest → spec/block_docs}/alt.md +2 -14
- data/{tests/unittest/pending → spec/block_docs}/amps.md +1 -13
- data/{tests/unittest → spec/block_docs}/attributes/att2.md +0 -12
- data/{tests/unittest → spec/block_docs}/attributes/att3.md +2 -14
- data/{tests/unittest → spec/block_docs}/attributes/attributes.md +12 -16
- data/{tests/unittest → spec/block_docs}/attributes/circular.md +0 -12
- data/{tests/unittest → spec/block_docs}/attributes/default.md +1 -13
- data/{tests/unittest → spec/block_docs}/blank.md +0 -12
- data/{tests/unittest → spec/block_docs}/blanks_in_code.md +16 -15
- data/{tests/unittest/loss.md → spec/block_docs/bug_def.md} +6 -18
- data/{tests/unittest → spec/block_docs}/bug_table.md +3 -15
- data/{tests/unittest → spec/block_docs}/code.md +7 -14
- data/{tests/unittest → spec/block_docs}/code2.md +4 -14
- data/{tests/unittest → spec/block_docs}/code3.md +12 -16
- data/{tests/unittest → spec/block_docs}/data_loss.md +2 -14
- data/{tests/unittest → spec/block_docs}/divs/div1.md +0 -12
- data/{tests/unittest → spec/block_docs}/divs/div2.md +0 -12
- data/{tests/unittest → spec/block_docs}/divs/div3_nest.md +3 -15
- data/{tests/unittest → spec/block_docs}/easy.md +1 -13
- data/spec/block_docs/email.md +29 -0
- data/{tests/unittest/pending → spec/block_docs}/empty_cells.md +3 -15
- data/{tests/unittest → spec/block_docs}/encoding/iso-8859-1.md +1 -14
- data/{tests/unittest → spec/block_docs}/encoding/utf-8.md +0 -12
- data/{tests/unittest → spec/block_docs}/entities.md +27 -29
- data/{tests/unittest/notyet → spec/block_docs}/escape.md +2 -14
- data/{tests/unittest → spec/block_docs}/escaping.md +11 -22
- data/{tests/unittest → spec/block_docs}/extra_dl.md +2 -13
- data/{tests/unittest → spec/block_docs}/extra_header_id.md +14 -20
- data/{tests/unittest → spec/block_docs}/extra_table1.md +3 -15
- data/spec/block_docs/fenced_code_blocks.md +66 -0
- data/spec/block_docs/fenced_code_blocks_highlighted.md +18 -0
- data/{tests/unittest → spec/block_docs}/footnotes.md +12 -24
- data/spec/block_docs/footnotes2.md +78 -0
- data/spec/block_docs/hard.md +25 -0
- data/spec/block_docs/header_after_par.md +62 -0
- data/{tests/unittest → spec/block_docs}/headers.md +10 -18
- data/{tests/unittest → spec/block_docs}/hex_entities.md +7 -18
- data/{tests/unittest → spec/block_docs}/hrule.md +5 -12
- data/{tests/unittest → spec/block_docs}/html3.md +1 -13
- data/{tests/unittest → spec/block_docs}/html4.md +2 -14
- data/{tests/unittest → spec/block_docs}/html5.md +2 -14
- data/spec/block_docs/html_block_in_para.md +22 -0
- data/spec/block_docs/html_inline.md +25 -0
- data/spec/block_docs/html_trailing.md +31 -0
- data/spec/block_docs/ie.md +62 -0
- data/spec/block_docs/iframe.md +29 -0
- data/{tests/unittest → spec/block_docs}/images.md +22 -28
- data/{tests/unittest → spec/block_docs}/images2.md +7 -17
- data/{tests/unittest → spec/block_docs}/inline_html.md +37 -67
- data/{tests/unittest → spec/block_docs}/inline_html2.md +1 -13
- data/spec/block_docs/inline_html_beginning.md +10 -0
- data/spec/block_docs/issue20.md +9 -0
- data/spec/block_docs/issue26.md +22 -0
- data/spec/block_docs/issue29.md +9 -0
- data/spec/block_docs/issue30.md +30 -0
- data/spec/block_docs/issue31.md +25 -0
- data/spec/block_docs/issue40.md +40 -0
- data/spec/block_docs/issue64.md +55 -0
- data/spec/block_docs/issue67.md +19 -0
- data/spec/block_docs/issue70.md +11 -0
- data/spec/block_docs/issue72.md +17 -0
- data/spec/block_docs/issue74.md +38 -0
- data/spec/block_docs/issue79.md +15 -0
- data/spec/block_docs/issue83.md +13 -0
- data/spec/block_docs/issue85.md +25 -0
- data/spec/block_docs/issue88.md +19 -0
- data/spec/block_docs/issue89.md +12 -0
- data/spec/block_docs/issue90.md +38 -0
- data/{tests/unittest/pending → spec/block_docs}/link.md +21 -18
- data/{tests/unittest → spec/block_docs}/links.md +33 -32
- data/spec/block_docs/links2.md +21 -0
- data/{tests/unittest → spec/block_docs}/list1.md +0 -12
- data/{tests/unittest → spec/block_docs}/list12.md +2 -14
- data/{tests/unittest → spec/block_docs}/list2.md +2 -14
- data/spec/block_docs/list_multipara.md +42 -0
- data/{tests/unittest → spec/block_docs}/lists.md +28 -29
- data/{tests/unittest → spec/block_docs}/lists10.md +2 -14
- data/spec/block_docs/lists11.md +23 -0
- data/spec/block_docs/lists12.md +43 -0
- data/spec/block_docs/lists13.md +55 -0
- data/spec/block_docs/lists14.md +61 -0
- data/spec/block_docs/lists15.md +36 -0
- data/spec/block_docs/lists6.md +88 -0
- data/spec/block_docs/lists7b.md +58 -0
- data/spec/block_docs/lists9.md +53 -0
- data/{tests/unittest → spec/block_docs}/lists_after_paragraph.md +19 -25
- data/spec/block_docs/lists_blank.md +35 -0
- data/{tests/unittest/list3.md → spec/block_docs/lists_blockquote_code.md} +2 -14
- data/{tests/unittest/list4.md → spec/block_docs/lists_need_blank_line.md} +50 -21
- data/spec/block_docs/lists_nested.md +44 -0
- data/spec/block_docs/lists_nested_blankline.md +28 -0
- data/spec/block_docs/lists_nested_deep.md +43 -0
- data/{tests/unittest → spec/block_docs}/lists_ol.md +37 -54
- data/spec/block_docs/lists_paraindent.md +47 -0
- data/spec/block_docs/lists_tab.md +54 -0
- data/spec/block_docs/loss.md +17 -0
- data/spec/block_docs/math-blahtex/equations.md +30 -0
- data/spec/block_docs/math-blahtex/inline.md +48 -0
- data/spec/block_docs/math-blahtex/math2.md +45 -0
- data/spec/block_docs/math-blahtex/table.md +25 -0
- data/spec/block_docs/math/embedded_invalid_svg.md +79 -0
- data/spec/block_docs/math/embedded_svg.md +97 -0
- data/spec/block_docs/math/equations.md +44 -0
- data/{tests/unittest → spec/block_docs}/math/inline.md +7 -19
- data/spec/block_docs/math/math2.md +45 -0
- data/{tests/unittest → spec/block_docs}/math/notmath.md +0 -12
- data/spec/block_docs/math/raw_mathml.md +87 -0
- data/spec/block_docs/math/table.md +25 -0
- data/{tests/unittest → spec/block_docs}/math/table2.md +5 -17
- data/{tests/unittest → spec/block_docs}/misc_sw.md +181 -118
- data/{tests/unittest → spec/block_docs}/olist.md +6 -18
- data/{tests/unittest → spec/block_docs}/one.md +0 -12
- data/{tests/unittest → spec/block_docs}/paragraph.md +0 -12
- data/{tests/unittest → spec/block_docs}/paragraph_rules/dont_merge_ref.md +4 -12
- data/{tests/unittest → spec/block_docs}/paragraph_rules/tab_is_blank.md +0 -12
- data/{tests/unittest → spec/block_docs}/paragraphs.md +1 -13
- data/{tests/unittest → spec/block_docs}/recover/recover_links.md +4 -16
- data/{tests/unittest/pending/ref.md → spec/block_docs/ref_with_period.md} +7 -16
- data/spec/block_docs/ref_with_title.md +22 -0
- data/{tests/unittest → spec/block_docs}/references/long_example.md +16 -23
- data/{tests/unittest → spec/block_docs}/references/spaces_and_numbers.md +0 -12
- data/{tests/unittest → spec/block_docs}/smartypants.md +24 -31
- data/{tests/unittest → spec/block_docs}/syntax_hl.md +13 -17
- data/{tests/unittest → spec/block_docs}/table_attributes.md +2 -14
- data/spec/block_docs/tables.md +58 -0
- data/{tests/unittest → spec/block_docs}/test.md +1 -13
- data/{tests/unittest/notyet → spec/block_docs}/ticks.md +1 -13
- data/spec/block_docs/toc.md +87 -0
- data/{tests/unittest/notyet → spec/block_docs}/triggering.md +14 -25
- data/{tests/unittest → spec/block_docs}/underscore_in_words.md +0 -12
- data/{tests/unittest → spec/block_docs}/wrapping.md +4 -16
- data/spec/block_docs/xml.md +33 -0
- data/{tests/unittest → spec/block_docs}/xml2.md +0 -12
- data/spec/block_docs/xml3.md +24 -0
- data/{tests/unittest → spec/block_docs}/xml_instruction.md +9 -20
- data/spec/block_spec.rb +110 -0
- data/spec/cli_spec.rb +8 -0
- data/spec/span_spec.rb +256 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/to_html_utf8_spec.rb +13 -0
- metadata +205 -243
- metadata.gz.sig +3 -0
- data/Rakefile +0 -48
- data/bin/marudown +0 -29
- data/bin/marutest +0 -345
- data/docs/changelog.md +0 -334
- data/lib/maruku/errors_management.rb +0 -92
- data/lib/maruku/ext/math/latex_fix.rb +0 -12
- data/lib/maruku/input/parse_span_better.rb +0 -746
- data/lib/maruku/input/type_detection.rb +0 -147
- data/lib/maruku/output/to_latex_entities.rb +0 -367
- data/lib/maruku/output/to_latex_strings.rb +0 -64
- data/lib/maruku/structures.rb +0 -167
- data/lib/maruku/structures_inspect.rb +0 -87
- data/lib/maruku/structures_iterators.rb +0 -61
- data/lib/maruku/tests/benchmark.rb +0 -82
- data/lib/maruku/tests/new_parser.rb +0 -373
- data/lib/maruku/tests/tests.rb +0 -136
- data/lib/maruku/usage/example1.rb +0 -33
- data/tests/bugs/code_in_links.md +0 -101
- data/tests/bugs/complex_escaping.md +0 -38
- data/tests/math/syntax.md +0 -46
- data/tests/math_usage/document.md +0 -13
- data/tests/others/abbreviations.md +0 -11
- data/tests/others/blank.md +0 -4
- data/tests/others/code.md +0 -5
- data/tests/others/code2.md +0 -8
- data/tests/others/code3.md +0 -16
- data/tests/others/email.md +0 -4
- data/tests/others/entities.md +0 -19
- data/tests/others/escaping.md +0 -16
- data/tests/others/extra_dl.md +0 -101
- data/tests/others/extra_header_id.md +0 -13
- data/tests/others/extra_table1.md +0 -40
- data/tests/others/footnotes.md +0 -17
- data/tests/others/headers.md +0 -10
- data/tests/others/hrule.md +0 -10
- data/tests/others/images.md +0 -20
- data/tests/others/inline_html.md +0 -42
- data/tests/others/links.md +0 -38
- data/tests/others/list1.md +0 -4
- data/tests/others/list2.md +0 -5
- data/tests/others/list3.md +0 -8
- data/tests/others/lists.md +0 -32
- data/tests/others/lists_after_paragraph.md +0 -44
- data/tests/others/lists_ol.md +0 -39
- data/tests/others/misc_sw.md +0 -105
- data/tests/others/one.md +0 -1
- data/tests/others/paragraphs.md +0 -13
- data/tests/others/sss06.md +0 -352
- data/tests/others/test.md +0 -4
- data/tests/s5/s5profiling.md +0 -48
- data/tests/unittest/bug_def.md +0 -28
- data/tests/unittest/email.md +0 -32
- data/tests/unittest/html2.md +0 -34
- data/tests/unittest/ie.md +0 -61
- data/tests/unittest/links2.md +0 -34
- data/tests/unittest/lists11.md +0 -28
- data/tests/unittest/lists6.md +0 -53
- data/tests/unittest/lists9.md +0 -76
- data/tests/unittest/math/equations.md +0 -86
- data/tests/unittest/math/math2.md +0 -57
- data/tests/unittest/math/table.md +0 -37
- data/tests/unittest/notyet/header_after_par.md +0 -70
- data/tests/unittest/red_tests/abbrev.md +0 -1388
- data/tests/unittest/red_tests/lists7.md +0 -68
- data/tests/unittest/red_tests/lists7b.md +0 -128
- data/tests/unittest/red_tests/lists8.md +0 -76
- data/tests/unittest/red_tests/xml.md +0 -70
- data/tests/unittest/xml3.md +0 -38
- data/tests/utf8-files/simple.md +0 -1
- data/unit_test_block.sh +0 -5
- data/unit_test_span.sh +0 -3
@@ -1,33 +1,16 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
#
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# GNU General Public License for more details.
|
15
|
-
#
|
16
|
-
# You should have received a copy of the GNU General Public License
|
17
|
-
# along with Maruku; if not, write to the Free Software
|
18
|
-
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
19
|
-
#++
|
20
|
-
|
21
|
-
module MaRuKu; module In; module Markdown; module BlockLevelParser
|
22
|
-
|
23
|
-
def parse_doc(s)
|
24
|
-
# FIXME \r\n => \n
|
25
|
-
meta2 = parse_email_headers(s)
|
26
|
-
data = meta2[:data]
|
27
|
-
meta2.delete :data
|
28
|
-
|
29
|
-
self.attributes.merge! meta2
|
30
|
-
|
1
|
+
require 'strscan'
|
2
|
+
require 'cgi'
|
3
|
+
|
4
|
+
module MaRuKu::In::Markdown::BlockLevelParser
|
5
|
+
|
6
|
+
def parse_doc(s)
|
7
|
+
# Remove BOM if it is present
|
8
|
+
s = s.sub(/^\xEF\xBB\xBF/u, '')
|
9
|
+
meta2 = parse_email_headers(s)
|
10
|
+
data = meta2.delete :data
|
11
|
+
|
12
|
+
self.attributes.merge! meta2
|
13
|
+
|
31
14
|
=begin maruku_doc
|
32
15
|
Attribute: encoding
|
33
16
|
Scope: document
|
@@ -37,50 +20,40 @@ If the `encoding` attribute is specified, then the content
|
|
37
20
|
will be converted from the specified encoding to UTF-8.
|
38
21
|
=end
|
39
22
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
# Now do the attributes magic
|
75
|
-
each_element do |e|
|
76
|
-
# default attribute list
|
77
|
-
if default = self.ald[e.node_type.to_s]
|
78
|
-
expand_attribute_list(default, e.attributes)
|
79
|
-
end
|
80
|
-
expand_attribute_list(e.al, e.attributes)
|
81
|
-
# puts "#{e.node_type}: #{e.attributes.inspect}"
|
82
|
-
end
|
83
|
-
|
23
|
+
enc = self.attributes.delete(:encoding) || 'utf-8'
|
24
|
+
if enc.downcase != 'utf-8'
|
25
|
+
# Switch to ruby 1.9 String#encode
|
26
|
+
# with backward 1.8 compatibility
|
27
|
+
if data.respond_to?(:encode!)
|
28
|
+
data.encode!('UTF-8', enc)
|
29
|
+
else
|
30
|
+
require 'iconv'
|
31
|
+
data = Iconv.new('utf-8', enc).iconv(data)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@children = parse_text_as_markdown(data)
|
36
|
+
|
37
|
+
if markdown_extra?
|
38
|
+
self.search_abbreviations
|
39
|
+
self.substitute_markdown_inside_raw_html
|
40
|
+
end
|
41
|
+
|
42
|
+
self.toc = create_toc
|
43
|
+
|
44
|
+
# use title if not set
|
45
|
+
self.attributes[:title] ||= toc.header_element.children.join if toc.header_element
|
46
|
+
|
47
|
+
# Now do the attributes magic
|
48
|
+
each_element do |e|
|
49
|
+
# default attribute list
|
50
|
+
if default = self.ald[e.node_type.to_s]
|
51
|
+
expand_attribute_list(default, e.attributes)
|
52
|
+
end
|
53
|
+
expand_attribute_list(e.al, e.attributes)
|
54
|
+
# puts "#{e.node_type}: #{e.attributes.inspect}"
|
55
|
+
end
|
56
|
+
|
84
57
|
=begin maruku_doc
|
85
58
|
Attribute: unsafe_features
|
86
59
|
Scope: global
|
@@ -89,144 +62,101 @@ Summary: Enables execution of XML instructions.
|
|
89
62
|
Disabled by default because of security concerns.
|
90
63
|
=end
|
91
64
|
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
doc = e.instance_variable_get :@parsed_html
|
191
|
-
if doc # valid html
|
192
|
-
# parse block-level markdown elements in these HTML tags
|
193
|
-
block_tags = ['div']
|
194
|
-
|
195
|
-
# use xpath to find elements with 'markdown' attribute
|
196
|
-
XPath.match(doc, "//*[attribute::markdown]" ).each do |e|
|
197
|
-
# puts "Found #{e}"
|
198
|
-
# should we parse block-level or span-level?
|
199
|
-
|
200
|
-
how = e.attributes['markdown']
|
201
|
-
parse_blocks = (how == 'block') || block_tags.include?(e.name)
|
202
|
-
|
203
|
-
# Select all text elements of e
|
204
|
-
XPath.match(e, "//text()" ).each { |original_text|
|
205
|
-
s = original_text.value.strip
|
206
|
-
if s.size > 0
|
207
|
-
|
208
|
-
# puts "Parsing #{s.inspect} as blocks: #{parse_blocks} (#{e.name}, #{e.attributes['markdown']}) "
|
209
|
-
|
210
|
-
el = md_el(:dummy,
|
211
|
-
parse_blocks ? parse_text_as_markdown(s) :
|
212
|
-
parse_lines_as_span([s]) )
|
213
|
-
p = original_text.parent
|
214
|
-
el.children_to_html.each do |x|
|
215
|
-
p.insert_before(original_text, x)
|
216
|
-
end
|
217
|
-
p.delete(original_text)
|
218
|
-
|
219
|
-
end
|
220
|
-
}
|
221
|
-
|
222
|
-
|
223
|
-
# remove 'markdown' attribute
|
224
|
-
e.delete_attribute 'markdown'
|
225
|
-
|
226
|
-
end
|
227
|
-
|
228
|
-
end
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
end end end end
|
65
|
+
if Maruku::Globals[:unsafe_features]
|
66
|
+
self.execute_code_blocks
|
67
|
+
# TODO: remove executed code blocks
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Expands an attribute list in an Hash
|
72
|
+
def expand_attribute_list(al, result)
|
73
|
+
al.each do |k, v|
|
74
|
+
case k
|
75
|
+
when :class
|
76
|
+
if result[:class]
|
77
|
+
result[:class] << " " << v
|
78
|
+
else
|
79
|
+
result[:class] = v
|
80
|
+
end
|
81
|
+
when :id
|
82
|
+
result[:id] = v
|
83
|
+
when :ref
|
84
|
+
if self.ald[v]
|
85
|
+
already = (result[:expanded_references] ||= [])
|
86
|
+
if !already.include?(v)
|
87
|
+
already << v
|
88
|
+
expand_attribute_list(self.ald[v], result)
|
89
|
+
else
|
90
|
+
already << v
|
91
|
+
maruku_error "Circular reference between labels.\n\n" +
|
92
|
+
"Label #{v.inspect} calls itself via recursion.\nThe recursion is " +
|
93
|
+
already.map(&:inspect).join(' => ')
|
94
|
+
end
|
95
|
+
else
|
96
|
+
if result[:unresolved_references]
|
97
|
+
result[:unresolved_references] << " " << v
|
98
|
+
else
|
99
|
+
result[:unresolved_references] = v
|
100
|
+
end
|
101
|
+
|
102
|
+
# $stderr.puts "Unresolved reference #{v.inspect} (avail: #{self.ald.keys.inspect})"
|
103
|
+
result[v.to_sym] = true
|
104
|
+
end
|
105
|
+
else
|
106
|
+
result[k.to_sym] = v
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def safe_execute_code(object, code)
|
112
|
+
begin
|
113
|
+
object.instance_eval(code)
|
114
|
+
rescue StandardError, ScriptError => e
|
115
|
+
maruku_error "Exception while executing this:\n" +
|
116
|
+
code.gsub(/^/, ">") +
|
117
|
+
"\nThe error was:\n" +
|
118
|
+
(e.inspect + "\n" + e.caller.join("\n")).gsub(/^/, "|")
|
119
|
+
nil
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def execute_code_blocks
|
124
|
+
each_element(:xml_instr) do |e|
|
125
|
+
if e.target == 'maruku'
|
126
|
+
result = safe_execute_code(e, e.code)
|
127
|
+
if result.kind_of?(String)
|
128
|
+
puts "Result is : #{result.inspect}"
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def search_abbreviations
|
135
|
+
abbreviations.each do |abbrev, title|
|
136
|
+
reg = Regexp.new(Regexp.escape(abbrev))
|
137
|
+
replace_each_string do |s|
|
138
|
+
# bug if many abbreviations are present (agorf)
|
139
|
+
p = StringScanner.new(s)
|
140
|
+
a = []
|
141
|
+
until p.eos?
|
142
|
+
o = ''
|
143
|
+
o << p.getch until p.scan(reg) or p.eos?
|
144
|
+
a << o unless o.empty?
|
145
|
+
a << md_abbr(abbrev.dup, title ? title.dup : nil) if p.matched == abbrev
|
146
|
+
end
|
147
|
+
a
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# (PHP Markdown extra) Search for elements that have
|
153
|
+
# markdown=1 or markdown=block defined
|
154
|
+
def substitute_markdown_inside_raw_html
|
155
|
+
each_element(:raw_html) do |e|
|
156
|
+
html = e.parsed_html
|
157
|
+
next unless html
|
158
|
+
|
159
|
+
html.process_markdown_inside_elements(self)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
@@ -0,0 +1,658 @@
|
|
1
|
+
module MaRuKu::In::Markdown::SpanLevelParser
|
2
|
+
include MaRuKu::Helpers
|
3
|
+
|
4
|
+
EscapedCharInText = '\\`*_{}[]()#.!|:+->'.split(//)
|
5
|
+
EscapedCharInQuotes = EscapedCharInText + ["'", '"']
|
6
|
+
|
7
|
+
EscapedCharInInlineCode = ['\\', '`']
|
8
|
+
|
9
|
+
IgnoreWikiLinks = MaRuKu::Globals[:ignore_wikilinks]
|
10
|
+
|
11
|
+
def parse_span(string, parent=nil)
|
12
|
+
string = Array(string).join("\n") unless string.kind_of? String
|
13
|
+
src = MaRuKu::In::Markdown::SpanLevelParser::CharSource.new(string, parent)
|
14
|
+
read_span(src, EscapedCharInText, [nil])
|
15
|
+
end
|
16
|
+
|
17
|
+
# This is the main loop for reading span elements
|
18
|
+
#
|
19
|
+
# It's long, but not *complex* or difficult to understand.
|
20
|
+
#
|
21
|
+
#
|
22
|
+
def read_span(src, escaped, exit_on_chars=nil, exit_on_strings=nil)
|
23
|
+
escaped = Array(escaped)
|
24
|
+
con = SpanContext.new
|
25
|
+
c = d = nil
|
26
|
+
while true
|
27
|
+
c = src.cur_char
|
28
|
+
|
29
|
+
# This is only an optimization which cuts 50% of the time used.
|
30
|
+
# (but you can't use a-zA-z in exit_on_chars)
|
31
|
+
if c && c =~ /a-zA-Z0-9/
|
32
|
+
con.push_char src.shift_char
|
33
|
+
next
|
34
|
+
end
|
35
|
+
|
36
|
+
break if Array(exit_on_chars).include?(c)
|
37
|
+
if Array(exit_on_strings).any? {|x| src.cur_chars_are x }
|
38
|
+
# Special case: bold nested in italic
|
39
|
+
break unless !(['*', '_'] & Array(exit_on_strings)).empty? &&
|
40
|
+
['**', '__'].include?(src.cur_chars(2)) &&
|
41
|
+
!['***', '___'].include?(src.cur_chars(3))
|
42
|
+
end
|
43
|
+
|
44
|
+
# check if there are extensions
|
45
|
+
next if check_span_extensions(src, con)
|
46
|
+
|
47
|
+
case c = src.cur_char
|
48
|
+
when ' '
|
49
|
+
if src.cur_chars_are " \n"
|
50
|
+
src.ignore_chars(3)
|
51
|
+
con.push_element md_br
|
52
|
+
next
|
53
|
+
else
|
54
|
+
src.ignore_char
|
55
|
+
con.push_space
|
56
|
+
end
|
57
|
+
when "\n", "\t"
|
58
|
+
src.ignore_char
|
59
|
+
con.push_space
|
60
|
+
when '`'
|
61
|
+
read_inline_code(src, con)
|
62
|
+
when '<'
|
63
|
+
# It could be:
|
64
|
+
# 1) HTML "<div ..."
|
65
|
+
# 2) HTML "<!-- ..."
|
66
|
+
# 3) url "<http:// ", "<ftp:// ..."
|
67
|
+
# 4) email "<andrea@... ", "<mailto:andrea@..."
|
68
|
+
# 5) on itself! "a < b "
|
69
|
+
# 6) Start of <<guillemettes>>
|
70
|
+
|
71
|
+
case d = src.next_char
|
72
|
+
when '<' # guillemettes
|
73
|
+
src.ignore_chars(2)
|
74
|
+
con.push_char '<'
|
75
|
+
con.push_char '<'
|
76
|
+
when '!'
|
77
|
+
if src.cur_chars_are '<!--'
|
78
|
+
read_inline_html(src, con)
|
79
|
+
else
|
80
|
+
con.push_char src.shift_char
|
81
|
+
end
|
82
|
+
when '?'
|
83
|
+
read_xml_instr_span(src, con)
|
84
|
+
when ' ', "\t"
|
85
|
+
con.push_char src.shift_char
|
86
|
+
else
|
87
|
+
if src.next_matches(/<mailto:/) ||
|
88
|
+
src.next_matches(/<[\w\.]+\@/)
|
89
|
+
read_email_el(src, con)
|
90
|
+
elsif src.next_matches(/<\w+:/)
|
91
|
+
read_url_el(src, con)
|
92
|
+
elsif src.next_matches(/<\w/)
|
93
|
+
#puts "This is HTML: #{src.cur_chars(20)}"
|
94
|
+
read_inline_html(src, con)
|
95
|
+
else
|
96
|
+
#puts "This is NOT HTML: #{src.cur_chars(20)}"
|
97
|
+
con.push_char src.shift_char
|
98
|
+
end
|
99
|
+
end
|
100
|
+
when "\\"
|
101
|
+
d = src.next_char
|
102
|
+
if d == "'"
|
103
|
+
src.ignore_chars(2)
|
104
|
+
con.push_element md_entity('apos')
|
105
|
+
elsif d == '"'
|
106
|
+
src.ignore_chars(2)
|
107
|
+
con.push_element md_entity('quot')
|
108
|
+
elsif escaped.include? d
|
109
|
+
src.ignore_chars(2)
|
110
|
+
con.push_char d
|
111
|
+
else
|
112
|
+
con.push_char src.shift_char
|
113
|
+
end
|
114
|
+
when '['
|
115
|
+
if markdown_extra? && src.next_char == '^'
|
116
|
+
read_footnote_ref(src,con)
|
117
|
+
elsif IgnoreWikiLinks && src.next_char == '['
|
118
|
+
con.push_char src.shift_char
|
119
|
+
con.push_char src.shift_char
|
120
|
+
else
|
121
|
+
read_link(src, con)
|
122
|
+
end
|
123
|
+
when '!'
|
124
|
+
if src.next_char == '['
|
125
|
+
read_image(src, con)
|
126
|
+
else
|
127
|
+
con.push_char src.shift_char
|
128
|
+
end
|
129
|
+
when '&'
|
130
|
+
# named references
|
131
|
+
if m = src.read_regexp(/\&(\w+);/)
|
132
|
+
con.push_element md_entity(m[1])
|
133
|
+
# numeric
|
134
|
+
elsif m = src.read_regexp(/\&\#(x)?(\w+);/)
|
135
|
+
num = m[1] ? m[2].hex : m[2].to_i
|
136
|
+
con.push_element md_entity(num)
|
137
|
+
else
|
138
|
+
con.push_char src.shift_char
|
139
|
+
end
|
140
|
+
when '*'
|
141
|
+
if !src.next_char
|
142
|
+
maruku_error "Opening * as last char.", src, con, 'Treating as literal'
|
143
|
+
con.push_char src.shift_char
|
144
|
+
else
|
145
|
+
follows = src.cur_chars(4)
|
146
|
+
if follows =~ /^\*\*\*[^\s\*]/
|
147
|
+
con.push_element read_emstrong(src, '***')
|
148
|
+
elsif follows =~ /^\*\*[^\s\*]/
|
149
|
+
con.push_element read_strong(src, '**')
|
150
|
+
elsif follows =~ /^\*[^\s\*]/
|
151
|
+
con.push_element read_em(src, '*')
|
152
|
+
else # * is just a normal char
|
153
|
+
con.push_char src.shift_char
|
154
|
+
end
|
155
|
+
end
|
156
|
+
when '_'
|
157
|
+
if !src.next_char
|
158
|
+
maruku_error "Opening _ as last char", src, con, 'Treating as literal'
|
159
|
+
con.push_char src.shift_char
|
160
|
+
else
|
161
|
+
# we don't want "mod_ruby" to start an emphasis
|
162
|
+
# so we start one only if
|
163
|
+
# 1) there's nothing else in the span (first char)
|
164
|
+
# or 2) the last char was a space
|
165
|
+
# or 3) the current string is empty
|
166
|
+
#if con.elements.empty? ||
|
167
|
+
if con.is_end?
|
168
|
+
# also, we check the next characters
|
169
|
+
follows = src.cur_chars(4)
|
170
|
+
if follows =~ /^\_\_\_[^\s\_]/
|
171
|
+
con.push_element read_emstrong(src, '___')
|
172
|
+
elsif follows =~ /^\_\_[^\s\_]/
|
173
|
+
con.push_element read_strong(src, '__')
|
174
|
+
elsif follows =~ /^\_[^\s\_]/
|
175
|
+
con.push_element read_em(src, '_')
|
176
|
+
else # _ is just a normal char
|
177
|
+
con.push_char src.shift_char
|
178
|
+
end
|
179
|
+
else
|
180
|
+
# _ is just a normal char
|
181
|
+
con.push_char src.shift_char
|
182
|
+
end
|
183
|
+
end
|
184
|
+
when '{' # extension
|
185
|
+
if ['#', '.', ':'].include? src.next_char
|
186
|
+
src.ignore_char # {
|
187
|
+
interpret_extension(src, con, '}')
|
188
|
+
src.ignore_char # }
|
189
|
+
else
|
190
|
+
con.push_char src.shift_char
|
191
|
+
end
|
192
|
+
when nil
|
193
|
+
maruku_error( ("Unclosed span (waiting for %s" +
|
194
|
+
"#{exit_on_strings.inspect})") %
|
195
|
+
[ exit_on_chars ? "#{exit_on_chars.inspect} or" : "" ],
|
196
|
+
src, con)
|
197
|
+
break
|
198
|
+
else # normal text
|
199
|
+
con.push_char src.shift_char
|
200
|
+
end # end case
|
201
|
+
end # end while true
|
202
|
+
|
203
|
+
con.push_string_if_present
|
204
|
+
|
205
|
+
# Assign IAL to elements
|
206
|
+
merge_ial(con.elements, src, con)
|
207
|
+
|
208
|
+
# Remove leading space
|
209
|
+
if (s = con.elements.first).kind_of? String
|
210
|
+
if s[0, 1] == ' '
|
211
|
+
con.elements[0] = s[1..-1]
|
212
|
+
end
|
213
|
+
con.elements.shift if s.empty?
|
214
|
+
end
|
215
|
+
|
216
|
+
# Remove final spaces
|
217
|
+
if (s = con.elements.last).kind_of? String
|
218
|
+
s.chop! if s[-1, 1] == ' '
|
219
|
+
con.elements.pop if s.empty?
|
220
|
+
end
|
221
|
+
|
222
|
+
educate(con.elements)
|
223
|
+
end
|
224
|
+
|
225
|
+
|
226
|
+
def read_xml_instr_span(src, con)
|
227
|
+
src.ignore_chars(2) # starting <?
|
228
|
+
|
229
|
+
# read target <?target code... ?>
|
230
|
+
target = if m = src.read_regexp(/^(\w+)/)
|
231
|
+
m[1]
|
232
|
+
else
|
233
|
+
# XML instructions are invalid without a target
|
234
|
+
''
|
235
|
+
end
|
236
|
+
|
237
|
+
delim = "?>"
|
238
|
+
|
239
|
+
code = read_simple(src, nil, nil, delim)
|
240
|
+
|
241
|
+
src.ignore_chars delim.size
|
242
|
+
|
243
|
+
code = (code || "").strip
|
244
|
+
con.push_element md_xml_instr(target, code)
|
245
|
+
end
|
246
|
+
|
247
|
+
# Start: cursor on character **after** '{'
|
248
|
+
# End: curson on '}' or EOF
|
249
|
+
def interpret_extension(src, con, break_on_chars=nil)
|
250
|
+
case src.cur_char
|
251
|
+
when ':'
|
252
|
+
src.ignore_char # :
|
253
|
+
extension_meta(src, con, break_on_chars)
|
254
|
+
when '#', '.'
|
255
|
+
extension_meta(src, con, break_on_chars)
|
256
|
+
else
|
257
|
+
stuff = read_simple(src, '}', break_on_chars)
|
258
|
+
if stuff =~ /^(\w+\s|[^\w])/
|
259
|
+
extension_id = $1.strip
|
260
|
+
|
261
|
+
maruku_recover "I don't know what to do with extension '#{extension_id}'\n" +
|
262
|
+
"I will treat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
|
263
|
+
else
|
264
|
+
maruku_recover "I will treat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
|
265
|
+
end
|
266
|
+
extension_meta(src, con, break_on_chars)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def extension_meta(src, con, break_on_chars=nil)
|
271
|
+
if m = src.read_regexp(/([^\s\:\"\'}]+?):/)
|
272
|
+
name = m[1]
|
273
|
+
al = read_attribute_list(src, con, break_on_chars)
|
274
|
+
self.doc.ald[name] = al
|
275
|
+
con.push md_ald(name, al)
|
276
|
+
else
|
277
|
+
al = read_attribute_list(src, con, break_on_chars)
|
278
|
+
con.push md_ial(al)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def read_url_el(src,con)
|
283
|
+
src.ignore_char # leading <
|
284
|
+
url = read_simple(src, nil, '>')
|
285
|
+
src.ignore_char # closing >
|
286
|
+
|
287
|
+
con.push_element md_url(url)
|
288
|
+
end
|
289
|
+
|
290
|
+
def read_email_el(src,con)
|
291
|
+
src.ignore_char # leading <
|
292
|
+
mail = read_simple(src, nil, '>')
|
293
|
+
src.ignore_char # closing >
|
294
|
+
|
295
|
+
address = mail.gsub(/^mailto:/, '')
|
296
|
+
con.push_element md_email(address)
|
297
|
+
end
|
298
|
+
|
299
|
+
def read_url(src, break_on)
|
300
|
+
if ["'", '"'].include? src.cur_char
|
301
|
+
maruku_error 'Invalid char for url', src
|
302
|
+
end
|
303
|
+
|
304
|
+
url = read_simple(src, nil, break_on) || ''
|
305
|
+
|
306
|
+
if url[0, 1] == '<' && url[-1, 1] == '>'
|
307
|
+
url = url[1, url.size-2]
|
308
|
+
end
|
309
|
+
|
310
|
+
return nil if url.empty?
|
311
|
+
url
|
312
|
+
end
|
313
|
+
|
314
|
+
|
315
|
+
def read_quoted_or_unquoted(src, con, escaped, exit_on_chars)
|
316
|
+
case src.cur_char
|
317
|
+
when "'", '"'
|
318
|
+
read_quoted(src, con)
|
319
|
+
else
|
320
|
+
read_simple(src, escaped, exit_on_chars, nil, false)
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# Tries to read a quoted value. If stream does not
|
325
|
+
# start with ' or ", returns nil.
|
326
|
+
def read_quoted(src, con)
|
327
|
+
case src.cur_char
|
328
|
+
when "'", '"'
|
329
|
+
quote_char = src.shift_char # opening quote
|
330
|
+
string = read_simple(src, EscapedCharInQuotes, quote_char)
|
331
|
+
src.ignore_char # closing quote
|
332
|
+
string
|
333
|
+
else
|
334
|
+
nil
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# Reads a simple string (no formatting) until one of break_on_chars,
|
339
|
+
# while escaping the escaped.
|
340
|
+
# If the string is empty, it returns nil.
|
341
|
+
# By default, raises on error if the string terminates unexpectedly. This can be
|
342
|
+
# by setting the last argument to false.
|
343
|
+
def read_simple(src, escaped, exit_on_chars=nil, exit_on_strings=nil, warn=true)
|
344
|
+
text = ""
|
345
|
+
escaped = Array(escaped)
|
346
|
+
exit_on_chars = Array(exit_on_chars)
|
347
|
+
exit_on_strings = Array(exit_on_strings)
|
348
|
+
while true
|
349
|
+
c = src.cur_char
|
350
|
+
|
351
|
+
break if exit_on_chars.include?(c)
|
352
|
+
break if exit_on_strings.any? {|x| src.cur_chars_are x }
|
353
|
+
|
354
|
+
case c
|
355
|
+
when nil
|
356
|
+
if warn
|
357
|
+
maruku_error "String finished while reading (break on " +
|
358
|
+
"#{exit_on_chars.inspect})" +
|
359
|
+
" already read: #{text.inspect}", src
|
360
|
+
end
|
361
|
+
break
|
362
|
+
when "\\"
|
363
|
+
d = src.next_char
|
364
|
+
if escaped.include? d
|
365
|
+
src.ignore_chars(2)
|
366
|
+
text << d
|
367
|
+
else
|
368
|
+
text << src.shift_char
|
369
|
+
end
|
370
|
+
else
|
371
|
+
text << src.shift_char
|
372
|
+
end
|
373
|
+
end
|
374
|
+
|
375
|
+
text.empty? ? nil : text
|
376
|
+
end
|
377
|
+
|
378
|
+
def read_em(src, delim)
|
379
|
+
src.ignore_char
|
380
|
+
children = read_span(src, EscapedCharInText, nil, delim)
|
381
|
+
src.ignore_char
|
382
|
+
md_em(children)
|
383
|
+
end
|
384
|
+
|
385
|
+
def read_strong(src, delim)
|
386
|
+
src.ignore_chars(2)
|
387
|
+
children = read_span(src, EscapedCharInText, nil, delim)
|
388
|
+
src.ignore_chars(2)
|
389
|
+
md_strong(children)
|
390
|
+
end
|
391
|
+
|
392
|
+
def read_emstrong(src, delim)
|
393
|
+
src.ignore_chars(3)
|
394
|
+
children = read_span(src, EscapedCharInText, nil, delim)
|
395
|
+
src.ignore_chars(3)
|
396
|
+
md_emstrong(children)
|
397
|
+
end
|
398
|
+
|
399
|
+
# Reads a bracketed id "[refid]". Consumes also both brackets.
|
400
|
+
def read_ref_id(src, con)
|
401
|
+
src.ignore_char # [
|
402
|
+
if m = src.read_regexp(/([^\]]*?)\]/)
|
403
|
+
m[1]
|
404
|
+
else
|
405
|
+
nil
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
def read_footnote_ref(src,con)
|
410
|
+
ref = read_ref_id(src,con)
|
411
|
+
con.push_element md_foot_ref(ref)
|
412
|
+
end
|
413
|
+
|
414
|
+
def read_inline_html(src, con)
|
415
|
+
h = HTMLHelper.new
|
416
|
+
begin
|
417
|
+
# This is our current buffer in the context
|
418
|
+
next_stuff = src.current_remaining_buffer
|
419
|
+
|
420
|
+
consumed = 0
|
421
|
+
while true
|
422
|
+
if consumed >= next_stuff.size
|
423
|
+
maruku_error "Malformed HTML starting at #{next_stuff.inspect}", src, con
|
424
|
+
break
|
425
|
+
end
|
426
|
+
|
427
|
+
h.eat_this next_stuff[consumed].chr
|
428
|
+
consumed += 1
|
429
|
+
break if h.is_finished?
|
430
|
+
end
|
431
|
+
src.ignore_chars(consumed)
|
432
|
+
con.push_element md_html(h.stuff_you_read)
|
433
|
+
rescue => e
|
434
|
+
maruku_error "Bad html: \n" +
|
435
|
+
e.inspect.gsub(/^/, '>'), src, con, "I will try to continue after bad HTML."
|
436
|
+
con.push_char src.shift_char
|
437
|
+
end
|
438
|
+
end
|
439
|
+
|
440
|
+
def read_inline_code(src, con)
|
441
|
+
# Count the number of ticks
|
442
|
+
num_ticks = 0
|
443
|
+
while src.cur_char == '`'
|
444
|
+
num_ticks += 1
|
445
|
+
src.ignore_char
|
446
|
+
end
|
447
|
+
# We will read until this string
|
448
|
+
end_string = "`" * num_ticks
|
449
|
+
|
450
|
+
code = read_simple(src, nil, nil, end_string)
|
451
|
+
|
452
|
+
# We didn't find a closing batch!
|
453
|
+
if !code || src.cur_char != '`'
|
454
|
+
con.push_element(end_string + (code || '')) and return
|
455
|
+
end
|
456
|
+
|
457
|
+
# We didn't find a closing batch!
|
458
|
+
if !code || src.cur_char != '`'
|
459
|
+
con.push_element(end_string + (code || ''))
|
460
|
+
return
|
461
|
+
end
|
462
|
+
|
463
|
+
# puts "Now I expects #{num_ticks} ticks: #{src.cur_chars(10).inspect}"
|
464
|
+
src.ignore_chars num_ticks
|
465
|
+
|
466
|
+
# Ignore at most one space
|
467
|
+
if num_ticks > 1 && code[0, 1] == ' '
|
468
|
+
code = code[1..-1]
|
469
|
+
end
|
470
|
+
|
471
|
+
# drop last space
|
472
|
+
if num_ticks > 1 && code[-1, 1] == ' '
|
473
|
+
code = code[0..-2]
|
474
|
+
end
|
475
|
+
|
476
|
+
# puts "Read `` code: #{code.inspect}; after: #{src.cur_chars(10).inspect} "
|
477
|
+
con.push_element md_code(code)
|
478
|
+
end
|
479
|
+
|
480
|
+
def read_link(src, con)
|
481
|
+
# we read the string and see what happens
|
482
|
+
src.ignore_char # opening bracket
|
483
|
+
children = read_span(src, EscapedCharInText, ']')
|
484
|
+
src.ignore_char # closing bracket
|
485
|
+
|
486
|
+
# ignore space
|
487
|
+
if src.cur_char == ' ' && ['[', '('].include?(src.next_char)
|
488
|
+
src.shift_char
|
489
|
+
end
|
490
|
+
|
491
|
+
case src.cur_char
|
492
|
+
when '('
|
493
|
+
src.ignore_char # opening (
|
494
|
+
src.consume_whitespace
|
495
|
+
url = read_url(src, [' ', "\t", ")"]) || ''
|
496
|
+
|
497
|
+
src.consume_whitespace
|
498
|
+
title = nil
|
499
|
+
if src.cur_char != ')' # we have a title
|
500
|
+
quote_char = src.cur_char
|
501
|
+
title = read_quoted(src, con)
|
502
|
+
|
503
|
+
if not title
|
504
|
+
maruku_error 'Must quote title', src, con
|
505
|
+
else
|
506
|
+
# Tries to read a title with quotes: 
|
507
|
+
# this is the most ugly thing in Markdown
|
508
|
+
unless src.next_matches(/\s*\)/)
|
509
|
+
# if there is not a closing par ), then read
|
510
|
+
# the rest and guess it's title with quotes
|
511
|
+
rest = read_simple(src, nil, ')', nil)
|
512
|
+
# chop the closing char
|
513
|
+
rest.chop!
|
514
|
+
title << quote_char << rest
|
515
|
+
end
|
516
|
+
end
|
517
|
+
end
|
518
|
+
src.consume_whitespace
|
519
|
+
closing = src.shift_char # closing )
|
520
|
+
if closing != ')'
|
521
|
+
maruku_error 'Unclosed link', src, con, "No closing ): I will not create" +
|
522
|
+
" the link for #{children.inspect}"
|
523
|
+
con.push_elements children
|
524
|
+
return
|
525
|
+
end
|
526
|
+
con.push_element md_im_link(children, url, title)
|
527
|
+
when '[' # link ref
|
528
|
+
ref_id = read_ref_id(src, con)
|
529
|
+
if ref_id
|
530
|
+
con.push_element md_link(children, ref_id)
|
531
|
+
else
|
532
|
+
maruku_error "Could not read ref_id", src, con, "I will not create the link for " +
|
533
|
+
"#{children.inspect}"
|
534
|
+
con.push_elements children
|
535
|
+
return
|
536
|
+
end
|
537
|
+
else # empty [link]
|
538
|
+
con.push_element md_link(children, nil)
|
539
|
+
end
|
540
|
+
end # read link
|
541
|
+
|
542
|
+
def read_image(src, con)
|
543
|
+
src.ignore_chars(2) # opening "!["
|
544
|
+
alt_text = read_span(src, EscapedCharInText, ']')
|
545
|
+
src.ignore_char # closing bracket
|
546
|
+
# ignore space
|
547
|
+
if src.cur_char == ' ' && ['[', '('].include?(src.next_char)
|
548
|
+
src.ignore_char
|
549
|
+
end
|
550
|
+
case src.cur_char
|
551
|
+
when '('
|
552
|
+
src.ignore_char # opening (
|
553
|
+
src.consume_whitespace
|
554
|
+
url = read_url(src, [' ', "\t", ')'])
|
555
|
+
unless url
|
556
|
+
maruku_error "Could not read url from #{src.cur_chars(10).inspect}", src, con
|
557
|
+
end
|
558
|
+
src.consume_whitespace
|
559
|
+
title = nil
|
560
|
+
if src.cur_char != ')' # we have a title
|
561
|
+
quote_char = src.cur_char
|
562
|
+
title = read_quoted(src, con)
|
563
|
+
if !title
|
564
|
+
maruku_error 'Must quote title', src, con
|
565
|
+
else
|
566
|
+
# Tries to read a title with quotes: 
|
567
|
+
# this is the most ugly thing in Markdown
|
568
|
+
if !src.next_matches(/\s*\)/)
|
569
|
+
# if there is not a closing par ), then read
|
570
|
+
# the rest and guess it's title with quotes
|
571
|
+
rest = read_simple(src, nil, ')', nil)
|
572
|
+
# chop the closing char
|
573
|
+
rest.chop!
|
574
|
+
title << quote_char << rest
|
575
|
+
end
|
576
|
+
end
|
577
|
+
end
|
578
|
+
src.consume_whitespace
|
579
|
+
closing = src.shift_char # closing )
|
580
|
+
if closing != ')'
|
581
|
+
maruku_error "Unclosed link: '#{closing}'" +
|
582
|
+
" Read url=#{url.inspect} title=#{title.inspect}", src, con
|
583
|
+
end
|
584
|
+
con.push_element md_im_image(alt_text, url, title)
|
585
|
+
when '[' # link ref
|
586
|
+
ref_id = read_ref_id(src, con)
|
587
|
+
if !ref_id # TODO: check around
|
588
|
+
maruku_error 'Reference not closed.', src, con
|
589
|
+
ref_id = ""
|
590
|
+
end
|
591
|
+
|
592
|
+
con.push_element md_image(alt_text, ref_id)
|
593
|
+
else # no stuff
|
594
|
+
ref_id = alt_text.join
|
595
|
+
con.push_element md_image(alt_text, ref_id)
|
596
|
+
end
|
597
|
+
end # read link
|
598
|
+
|
599
|
+
class SpanContext
|
600
|
+
# Read elements
|
601
|
+
attr_accessor :elements
|
602
|
+
|
603
|
+
def initialize
|
604
|
+
@elements = []
|
605
|
+
@cur_string = ''
|
606
|
+
end
|
607
|
+
|
608
|
+
def push_element(e)
|
609
|
+
raise "Only MDElement and String, please. You pushed #{e.class}: #{e.inspect} " unless
|
610
|
+
e.kind_of?(String) || e.kind_of?(MaRuKu::MDElement)
|
611
|
+
|
612
|
+
push_string_if_present
|
613
|
+
|
614
|
+
@elements << e
|
615
|
+
end
|
616
|
+
alias push push_element
|
617
|
+
|
618
|
+
def push_elements(a)
|
619
|
+
a.each do |e|
|
620
|
+
if e.kind_of? String
|
621
|
+
@cur_string << e
|
622
|
+
else
|
623
|
+
push_element e
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
|
628
|
+
def is_end?
|
629
|
+
@cur_string.empty? || @cur_string =~ /\s\z/
|
630
|
+
end
|
631
|
+
|
632
|
+
def push_string_if_present
|
633
|
+
unless @cur_string.empty?
|
634
|
+
@elements << @cur_string
|
635
|
+
@cur_string = ''
|
636
|
+
end
|
637
|
+
end
|
638
|
+
|
639
|
+
def push_char(c)
|
640
|
+
@cur_string << c
|
641
|
+
end
|
642
|
+
|
643
|
+
# push space into current string if
|
644
|
+
# there isn't one
|
645
|
+
def push_space
|
646
|
+
@cur_string << ' ' unless @cur_string[-1, 1] == ' '
|
647
|
+
end
|
648
|
+
|
649
|
+
def describe
|
650
|
+
lines = @elements.map{|x| x.inspect }.join("\n")
|
651
|
+
s = "Elements read in span: \n" +
|
652
|
+
lines.gsub(/^/, ' -') + "\n"
|
653
|
+
|
654
|
+
s += "Current string: \n #{@cur_string.inspect}\n" unless @cur_string.empty?
|
655
|
+
s
|
656
|
+
end
|
657
|
+
end
|
658
|
+
end
|