maruku 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/maruku +41 -25
- data/docs/char.html +1924 -0
- data/docs/entity_test.html +2 -2
- data/docs/exd.html +92 -0
- data/docs/index.html +77 -23
- data/docs/markdown_syntax.html +6 -6
- data/docs/maruku.html +75 -21
- data/docs/maruku.md +60 -26
- data/docs/proposal.html +105 -123
- data/docs/proposal.md +121 -109
- data/lib/maruku/attributes.rb +33 -1
- data/lib/maruku/defaults.rb +8 -0
- data/lib/maruku/input/charsource.rb +8 -2
- data/lib/maruku/input/parse_block.rb +44 -18
- data/lib/maruku/input/parse_doc.rb +28 -26
- data/lib/maruku/input/parse_span_better.rb +57 -41
- data/lib/maruku/input/type_detection.rb +3 -2
- data/lib/maruku/output/to_html.rb +6 -6
- data/lib/maruku/output/to_latex.rb +21 -5
- data/lib/maruku/structures.rb +0 -4
- data/lib/maruku/tests/new_parser.rb +6 -1
- data/lib/maruku/version.rb +1 -1
- data/tests/unittest/attributes/att2.md +36 -0
- data/tests/unittest/attributes/att3.md +55 -0
- data/tests/unittest/attributes/attributes.md +23 -18
- data/tests/unittest/attributes/circular.md +9 -9
- data/tests/unittest/email.md +2 -2
- data/tests/unittest/links.md +2 -2
- data/tests/unittest/misc_sw.md +1 -1
- data/tests/unittest/syntax_hl.md +3 -5
- data/tests/unittest/xml_instruction.md +1 -2
- metadata +6 -2
@@ -41,9 +41,11 @@ CharSource = CharSourceManual # faster! 58ms vs. 65ms
|
|
41
41
|
class CharSourceManual
|
42
42
|
include MaRuKu::Strings
|
43
43
|
|
44
|
-
def initialize(s)
|
44
|
+
def initialize(s, parent=nil)
|
45
|
+
raise "Passed #{s.class}" if not s.kind_of? String
|
45
46
|
@buffer = s
|
46
47
|
@buffer_index = 0
|
48
|
+
@parent = parent
|
47
49
|
end
|
48
50
|
|
49
51
|
# Return current char as a FixNum (or nil).
|
@@ -140,7 +142,11 @@ class CharSourceManual
|
|
140
142
|
end
|
141
143
|
|
142
144
|
def describe
|
143
|
-
describe_pos(@buffer, @buffer_index)
|
145
|
+
s = describe_pos(@buffer, @buffer_index)
|
146
|
+
if @parent
|
147
|
+
s += "\n\n" + @parent.describe
|
148
|
+
end
|
149
|
+
s
|
144
150
|
end
|
145
151
|
include SpanLevelParser
|
146
152
|
end
|
@@ -24,6 +24,15 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
24
24
|
include Helpers
|
25
25
|
include MaRuKu::Strings
|
26
26
|
include MaRuKu::In::Markdown::SpanLevelParser
|
27
|
+
|
28
|
+
class BlockContext < Array
|
29
|
+
def describe
|
30
|
+
n = 5
|
31
|
+
desc = size > n ? self[-n,n] : self
|
32
|
+
"Last #{n} elements: "+
|
33
|
+
desc.map{|x| "\n -" + x.inspect}.join
|
34
|
+
end
|
35
|
+
end
|
27
36
|
|
28
37
|
# Splits the string and calls parse_lines_as_markdown
|
29
38
|
def parse_text_as_markdown(text)
|
@@ -32,25 +41,23 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
32
41
|
return parse_blocks(src)
|
33
42
|
end
|
34
43
|
|
44
|
+
# Input is a LineSource
|
35
45
|
def parse_blocks(src)
|
36
|
-
output =
|
46
|
+
output = BlockContext.new
|
37
47
|
|
38
48
|
# run state machine
|
39
49
|
while src.cur_line
|
40
50
|
# Prints detected type (useful for debugging)
|
41
|
-
|
51
|
+
# puts "#{src.cur_line.md_type}|#{src.cur_line}"
|
42
52
|
case src.cur_line.md_type
|
43
53
|
when :empty;
|
54
|
+
output.push :empty
|
44
55
|
src.ignore_line
|
45
56
|
when :ial
|
46
|
-
m =
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
else
|
51
|
-
maruku_error "An attribute list at beginning of context {#{al.to_md}}", src
|
52
|
-
maruku_recover "I will ignore this AL: {#{al.to_md}}", src
|
53
|
-
end
|
57
|
+
m = InlineAttributeList.match src.shift_line
|
58
|
+
content = m[1] || ""
|
59
|
+
src2 = CharSource.new(content, src)
|
60
|
+
interpret_extension(src2, output, [nil])
|
54
61
|
when :ald
|
55
62
|
output.push read_ald(src)
|
56
63
|
when :text
|
@@ -61,7 +68,8 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
61
68
|
output.push read_header12(src)
|
62
69
|
elsif eventually_comes_a_def_list(src)
|
63
70
|
definition = read_definition(src)
|
64
|
-
if output.last &&
|
71
|
+
if output.last.kind_of?(MDElement) &&
|
72
|
+
output.last.node_type == :definition_list then
|
65
73
|
output.last.children << definition
|
66
74
|
else
|
67
75
|
output.push md_el(:definition_list, [definition])
|
@@ -79,7 +87,8 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
79
87
|
list_type = src.cur_line.md_type == :ulist ? :ul : :ol
|
80
88
|
li = read_list_item(src)
|
81
89
|
# append to current list if we have one
|
82
|
-
if output.last &&
|
90
|
+
if output.last.kind_of?(MDElement) &&
|
91
|
+
output.last.node_type == list_type then
|
83
92
|
output.last.children << li
|
84
93
|
else
|
85
94
|
output.push md_el(list_type, [li])
|
@@ -91,7 +100,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
91
100
|
when :footnote_text; output.push read_footnote_text(src)
|
92
101
|
when :ref_definition; output.push read_ref_definition(src)
|
93
102
|
when :abbreviation; output.push read_abbreviation(src)
|
94
|
-
when :xml_instr;
|
103
|
+
when :xml_instr; read_xml_instruction(src, output)
|
95
104
|
# # these do not produce output
|
96
105
|
when :metadata;
|
97
106
|
maruku_error "Please use the new meta-data syntax: \n"+
|
@@ -104,7 +113,13 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
104
113
|
src.shift_line
|
105
114
|
end
|
106
115
|
end
|
116
|
+
|
117
|
+
merge_ial(output, src, output)
|
118
|
+
output.delete_if {|x| x.kind_of?(MDElement) &&
|
119
|
+
x.node_type == :ial}
|
107
120
|
|
121
|
+
# get rid of empty line markers
|
122
|
+
output.delete_if {|x| x == :empty}
|
108
123
|
# See for each list if we can omit the paragraphs and use li_span
|
109
124
|
# TODO: do this after
|
110
125
|
output.each do |c|
|
@@ -135,7 +150,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
135
150
|
def read_ald(src)
|
136
151
|
if (l=src.shift_line) =~ AttributeDefinitionList
|
137
152
|
id = $1; al=$2;
|
138
|
-
al = read_attribute_list(CharSource.new(al), context=nil, break_on=[nil])
|
153
|
+
al = read_attribute_list(CharSource.new(al,src), context=nil, break_on=[nil])
|
139
154
|
self.ald[id] = al;
|
140
155
|
return md_ald(id, al)
|
141
156
|
else
|
@@ -152,7 +167,7 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
152
167
|
if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
|
153
168
|
line = $1.strip
|
154
169
|
ial = $2
|
155
|
-
al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
|
170
|
+
al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
|
156
171
|
end
|
157
172
|
text = parse_lines_as_span [ line ]
|
158
173
|
level = src.cur_line.md_type == :header2 ? 2 : 1;
|
@@ -168,14 +183,14 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
168
183
|
if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
|
169
184
|
line = $1.strip
|
170
185
|
ial = $2
|
171
|
-
al = read_attribute_list(CharSource.new(ial), context=nil, break_on=[nil])
|
186
|
+
al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
|
172
187
|
end
|
173
188
|
level = num_leading_hashes(line)
|
174
189
|
text = parse_lines_as_span [strip_hashes(line)]
|
175
190
|
return md_header(level, text, al)
|
176
191
|
end
|
177
192
|
|
178
|
-
def read_xml_instruction(src)
|
193
|
+
def read_xml_instruction(src, output)
|
179
194
|
m = /^\s*<\?((\w+)\s*)?(.*)$/.match src.shift_line
|
180
195
|
raise "BugBug" if not m
|
181
196
|
target = m[2] || ''
|
@@ -190,7 +205,18 @@ module MaRuKu; module In; module Markdown; module BlockLevelParser
|
|
190
205
|
end
|
191
206
|
code.gsub!(/\?>\s*$/, '')
|
192
207
|
|
193
|
-
|
208
|
+
if target == 'mrk' && MaRuKu::Globals[:unsafe_features]
|
209
|
+
result = safe_execute_code(self, code)
|
210
|
+
if result
|
211
|
+
if result.kind_of? String
|
212
|
+
raise "Not expected"
|
213
|
+
else
|
214
|
+
output.push *result
|
215
|
+
end
|
216
|
+
end
|
217
|
+
else
|
218
|
+
output.push md_xml_instr(target, code)
|
219
|
+
end
|
194
220
|
end
|
195
221
|
|
196
222
|
def read_raw_html(src)
|
@@ -84,8 +84,7 @@ Conversion happens using the `iconv` library.
|
|
84
84
|
end
|
85
85
|
|
86
86
|
=begin maruku_doc
|
87
|
-
|
88
|
-
Scope: document
|
87
|
+
Variable: Maruku::Globals[:unsafe_features]
|
89
88
|
Summary: Enables execution of XML instructions.
|
90
89
|
|
91
90
|
Disabled by default because of security concerns.
|
@@ -115,8 +114,10 @@ Disabled by default because of security concerns.
|
|
115
114
|
already.push v
|
116
115
|
expand_attribute_list(self.ald[v], result)
|
117
116
|
else
|
118
|
-
|
119
|
-
|
117
|
+
already.push v
|
118
|
+
maruku_error "Circular reference between labels.\n\n"+
|
119
|
+
"Label #{v.inspect} calls itself via recursion.\nThe recursion is "+
|
120
|
+
(already.map{|x| x.inspect}.join(' => '))
|
120
121
|
end
|
121
122
|
else
|
122
123
|
if not result[:unresolved_references]
|
@@ -133,31 +134,32 @@ Disabled by default because of security concerns.
|
|
133
134
|
end
|
134
135
|
end
|
135
136
|
|
137
|
+
def safe_execute_code(object, code)
|
138
|
+
begin
|
139
|
+
return object.instance_eval(code)
|
140
|
+
rescue Exception => e
|
141
|
+
maruku_error "Exception while executing this:\n"+
|
142
|
+
add_tabs(code, 1, ">")+
|
143
|
+
"\nThe error was:\n"+
|
144
|
+
add_tabs(e.inspect+"\n"+e.caller.join("\n"), 1, "|")
|
145
|
+
rescue RuntimeError => e
|
146
|
+
maruku_error "2: Exception while executing this:\n"+
|
147
|
+
add_tabs(code, 1, ">")+
|
148
|
+
"\nThe error was:\n"+
|
149
|
+
add_tabs(e.inspect, 1, "|")
|
150
|
+
rescue SyntaxError => e
|
151
|
+
maruku_error "2: Exception while executing this:\n"+
|
152
|
+
add_tabs(code, 1, ">")+
|
153
|
+
"\nThe error was:\n"+
|
154
|
+
add_tabs(e.inspect, 1, "|")
|
155
|
+
end
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
|
136
159
|
def execute_code_blocks
|
137
160
|
self.each_element(:xml_instr) do |e|
|
138
161
|
if e.target == 'maruku'
|
139
|
-
|
140
|
-
code = e.code
|
141
|
-
result = nil
|
142
|
-
begin
|
143
|
-
e.instance_eval(code)
|
144
|
-
rescue Exception => e
|
145
|
-
maruku_error "Exception while executing this:\n"+
|
146
|
-
add_tabs(code, 1, ">")+
|
147
|
-
"\nThe error was:\n"+
|
148
|
-
add_tabs(e.inspect+"\n"+e.caller.join("\n"), 1, "|")
|
149
|
-
next
|
150
|
-
rescue RuntimeError => e
|
151
|
-
maruku_error "2: Exception while executing this:\n"+
|
152
|
-
add_tabs(code, 1, ">")+
|
153
|
-
"\nThe error was:\n"+
|
154
|
-
add_tabs(e.inspect, 1, "|")
|
155
|
-
rescue SyntaxError => e
|
156
|
-
maruku_error "2: Exception while executing this:\n"+
|
157
|
-
add_tabs(code, 1, ">")+
|
158
|
-
"\nThe error was:\n"+
|
159
|
-
add_tabs(e.inspect, 1, "|")
|
160
|
-
end
|
162
|
+
result = safe_execute_code(e, e.code)
|
161
163
|
if result.kind_of?(String)
|
162
164
|
puts "Result is : #{result.inspect}"
|
163
165
|
end
|
@@ -186,54 +186,26 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
|
|
186
186
|
con.push_char src.shift_char
|
187
187
|
end
|
188
188
|
end
|
189
|
-
when ?{ #
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
con.push_element ial
|
196
|
-
else # normal text
|
197
|
-
con.push_char src.shift_char
|
198
|
-
end
|
189
|
+
when ?{ # extension
|
190
|
+
src.ignore_char # {
|
191
|
+
interpret_extension(src, con, [?}])
|
192
|
+
src.ignore_char # }
|
193
|
+
|
199
194
|
when nil
|
200
195
|
maruku_error ("Unclosed span (waiting for %s"+
|
201
196
|
"#{exit_on_strings.inspect})") % [
|
202
197
|
exit_on_chars ? "#{exit_on_chars.inspect} or" : ""],
|
203
198
|
src,con
|
204
|
-
|
205
199
|
break
|
206
200
|
else # normal text
|
207
201
|
con.push_char src.shift_char
|
208
202
|
end # end case
|
209
203
|
end # end while true
|
210
204
|
con.push_string_if_present
|
211
|
-
|
212
|
-
# Now we handle the IAL stuff
|
213
|
-
# We need a helper
|
214
|
-
def is_ial(e); e.kind_of? MDElement and e.node_type == :ial end
|
215
|
-
# A IAL at the beginning is strange and we ignore it
|
216
|
-
if false && is_ial(e=con.elements[0])
|
217
|
-
"Attribute list at the beginning of span {#{e.to_md}}"
|
218
|
-
tell_user "Ignoring {#{e.to_md}}"
|
219
|
-
con.elements.shift
|
220
|
-
end
|
221
|
-
# Apply each IAL to the element before
|
222
|
-
con.elements.each_with_index do |e, i| if is_ial(e) && i>= 1 then
|
223
|
-
before = con.elements[i-1]
|
224
|
-
if before.kind_of? MDElement
|
225
|
-
#puts "Assigning #{e.ial} to #{before}"
|
226
|
-
before.al = e.ial
|
227
|
-
else
|
228
|
-
maruku_error "This IAL: {#{e.ial.to_md}} seems to"+
|
229
|
-
" refer to a string:\n"+
|
230
|
-
before.inspect, src, con
|
231
|
-
maruku_recover "Ignoring IAL: {#{e.ial.to_md}}", src, con
|
232
|
-
end
|
233
|
-
end end
|
234
205
|
|
235
|
-
#
|
236
|
-
|
206
|
+
# Assign IAL to elements
|
207
|
+
merge_ial(con.elements, src, con)
|
208
|
+
|
237
209
|
|
238
210
|
# Remove leading space
|
239
211
|
if (s = con.elements.first).kind_of? String
|
@@ -251,7 +223,8 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
|
|
251
223
|
|
252
224
|
educated
|
253
225
|
end
|
254
|
-
|
226
|
+
|
227
|
+
|
255
228
|
def read_xml_instr_span(src, con)
|
256
229
|
src.ignore_chars(2) # starting <?
|
257
230
|
|
@@ -274,6 +247,46 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
|
|
274
247
|
con.push_element md_xml_instr(target, code)
|
275
248
|
end
|
276
249
|
|
250
|
+
# Start: cursor on character **after** '{'
|
251
|
+
# End: curson on '}' or EOF
|
252
|
+
def interpret_extension(src, con, break_on_chars)
|
253
|
+
case src.cur_char
|
254
|
+
when ?:
|
255
|
+
src.ignore_char # :
|
256
|
+
extension_meta(src, con, break_on_chars)
|
257
|
+
when ?#, ?.
|
258
|
+
extension_meta(src, con, break_on_chars)
|
259
|
+
else
|
260
|
+
stuff = read_simple(src, escaped=[?}], break_on_chars, [])
|
261
|
+
if stuff =~ /^(\w+\s|[^\w])/
|
262
|
+
extension_id = $1.strip
|
263
|
+
if false
|
264
|
+
else
|
265
|
+
maruku_recover "I don't know what to do with extension '#{extension_id}'\n"+
|
266
|
+
"I will threat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
|
267
|
+
extension_meta(src, con, break_on_chars)
|
268
|
+
end
|
269
|
+
else
|
270
|
+
maruku_recover "I will threat this:\n\t{#{stuff}} \n as meta-data.\n", src, con
|
271
|
+
extension_meta(src, con, break_on_chars)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def extension_meta(src, con, break_on_chars)
|
277
|
+
if m = src.read_regexp(/(\w)+\:/)
|
278
|
+
name = m[1]
|
279
|
+
content = m[2]
|
280
|
+
al = read_attribute_list(src, con, break_on_chars)
|
281
|
+
self.doc.ald[name] = al
|
282
|
+
con.push md_ald(name, al)
|
283
|
+
else
|
284
|
+
al = read_attribute_list(src, con, break_on_chars)
|
285
|
+
self.doc.ald[name] = al
|
286
|
+
con.push md_ial(al)
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
277
290
|
def read_url_el(src,con)
|
278
291
|
src.ignore_char # leading <
|
279
292
|
url = read_simple(src, [], [?>])
|
@@ -340,14 +353,16 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
|
|
340
353
|
# Reads a simple string (no formatting) until one of break_on_chars,
|
341
354
|
# while escaping the escaped.
|
342
355
|
# If the string is empty, it returns nil.
|
343
|
-
# Raises on error.
|
344
|
-
|
356
|
+
# Raises on error if the string terminates unexpectedly.
|
357
|
+
# # If eat_delim is true, and if the delim is not the EOF, then the delim
|
358
|
+
# # gets eaten from the stream.
|
359
|
+
def read_simple(src, escaped, exit_on_chars, exit_on_strings=nil)
|
345
360
|
text = ""
|
346
361
|
while true
|
347
362
|
# puts "Reading simple #{text.inspect}"
|
348
363
|
c = src.cur_char
|
349
364
|
if exit_on_chars && exit_on_chars.include?(c)
|
350
|
-
#
|
365
|
+
# src.ignore_char if eat_delim
|
351
366
|
break
|
352
367
|
end
|
353
368
|
|
@@ -625,7 +640,8 @@ module MaRuKu; module In; module Markdown; module SpanLevelParser
|
|
625
640
|
@elements << e
|
626
641
|
nil
|
627
642
|
end
|
628
|
-
|
643
|
+
alias push push_element
|
644
|
+
|
629
645
|
def push_elements(a)
|
630
646
|
for e in a
|
631
647
|
if e.kind_of? String
|
@@ -62,14 +62,15 @@ module MaRuKu; module Strings
|
|
62
62
|
return :metadata if l =~ /^@/
|
63
63
|
# if @@new_meta_data?
|
64
64
|
return :ald if l =~ AttributeDefinitionList
|
65
|
-
return :ial if l =~
|
65
|
+
return :ial if l =~ InlineAttributeList
|
66
66
|
# end
|
67
67
|
return :text # else, it's just text
|
68
68
|
end
|
69
69
|
|
70
70
|
# $1 = id $2 = attribute list
|
71
71
|
AttributeDefinitionList = /^\s{0,3}\{([\w\d\s]+)\}:\s*(.*)\s*$/
|
72
|
-
|
72
|
+
#
|
73
|
+
InlineAttributeList = /^\s{0,3}\{(.*)\}\s*$/
|
73
74
|
# Example:
|
74
75
|
# ^:blah blah
|
75
76
|
# ^: blah blah
|
@@ -346,7 +346,7 @@ module MaRuKu; module Out; module HTML
|
|
346
346
|
end
|
347
347
|
|
348
348
|
def to_html_code_using_pre(source)
|
349
|
-
pre =
|
349
|
+
pre = create_html_element 'pre'
|
350
350
|
code = Element.new 'code', pre
|
351
351
|
s = source
|
352
352
|
|
@@ -368,7 +368,7 @@ module MaRuKu; module Out; module HTML
|
|
368
368
|
end
|
369
369
|
|
370
370
|
def to_html_inline_code;
|
371
|
-
pre =
|
371
|
+
pre = create_html_element 'code'
|
372
372
|
source = self.raw_code
|
373
373
|
pre << source2html(source)
|
374
374
|
|
@@ -381,7 +381,7 @@ module MaRuKu; module Out; module HTML
|
|
381
381
|
end
|
382
382
|
|
383
383
|
def to_html_immediate_link
|
384
|
-
a =
|
384
|
+
a = create_html_element 'a'
|
385
385
|
url = self.url
|
386
386
|
text = url.gsub(/^mailto:/,'') # don't show mailto
|
387
387
|
a << Text.new(text)
|
@@ -439,7 +439,7 @@ module MaRuKu; module Out; module HTML
|
|
439
439
|
|
440
440
|
def to_html_email_address
|
441
441
|
email = self.email
|
442
|
-
a =
|
442
|
+
a = create_html_element 'a'
|
443
443
|
#a.attributes['href'] = Text.new("mailto:"+obfuscate(email),false,nil,true)
|
444
444
|
#a.attributes.add Attribute.new('href',Text.new(
|
445
445
|
#"mailto:"+obfuscate(email),false,nil,true))
|
@@ -453,7 +453,7 @@ module MaRuKu; module Out; module HTML
|
|
453
453
|
##### Images
|
454
454
|
|
455
455
|
def to_html_image
|
456
|
-
a =
|
456
|
+
a = create_html_element 'img'
|
457
457
|
id = self.ref_id
|
458
458
|
if ref = @doc.refs[id]
|
459
459
|
url = ref[:url]
|
@@ -478,7 +478,7 @@ module MaRuKu; module Out; module HTML
|
|
478
478
|
return wrap_as_element('span')
|
479
479
|
end
|
480
480
|
title = self.title
|
481
|
-
a =
|
481
|
+
a = create_html_element 'img'
|
482
482
|
a.attributes['src'] = url
|
483
483
|
a.attributes['title'] = title if title
|
484
484
|
return a
|