maiku 0.6.1.maiku
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.
- data/lib/maruku.rb +141 -0
- data/lib/maruku/attributes.rb +175 -0
- data/lib/maruku/defaults.rb +71 -0
- data/lib/maruku/errors_management.rb +92 -0
- data/lib/maruku/ext/div.rb +133 -0
- data/lib/maruku/ext/math.rb +41 -0
- data/lib/maruku/ext/math/elements.rb +27 -0
- data/lib/maruku/ext/math/latex_fix.rb +12 -0
- data/lib/maruku/ext/math/mathml_engines/blahtex.rb +107 -0
- data/lib/maruku/ext/math/mathml_engines/itex2mml.rb +29 -0
- data/lib/maruku/ext/math/mathml_engines/none.rb +20 -0
- data/lib/maruku/ext/math/mathml_engines/ritex.rb +24 -0
- data/lib/maruku/ext/math/parsing.rb +119 -0
- data/lib/maruku/ext/math/to_html.rb +187 -0
- data/lib/maruku/ext/math/to_latex.rb +26 -0
- data/lib/maruku/helpers.rb +260 -0
- data/lib/maruku/input/charsource.rb +326 -0
- data/lib/maruku/input/extensions.rb +69 -0
- data/lib/maruku/input/html_helper.rb +189 -0
- data/lib/maruku/input/linesource.rb +111 -0
- data/lib/maruku/input/parse_block.rb +616 -0
- data/lib/maruku/input/parse_doc.rb +232 -0
- data/lib/maruku/input/parse_span_better.rb +746 -0
- data/lib/maruku/input/rubypants.rb +225 -0
- data/lib/maruku/input/type_detection.rb +147 -0
- data/lib/maruku/input_textile2/t2_parser.rb +163 -0
- data/lib/maruku/maruku.rb +33 -0
- data/lib/maruku/output/s5/fancy.rb +756 -0
- data/lib/maruku/output/s5/to_s5.rb +138 -0
- data/lib/maruku/output/to_html.rb +991 -0
- data/lib/maruku/output/to_latex.rb +590 -0
- data/lib/maruku/output/to_latex_entities.rb +367 -0
- data/lib/maruku/output/to_latex_strings.rb +64 -0
- data/lib/maruku/output/to_markdown.rb +164 -0
- data/lib/maruku/output/to_s.rb +56 -0
- data/lib/maruku/string_utils.rb +201 -0
- data/lib/maruku/structures.rb +167 -0
- data/lib/maruku/structures_inspect.rb +87 -0
- data/lib/maruku/structures_iterators.rb +61 -0
- data/lib/maruku/textile2.rb +1 -0
- data/lib/maruku/toc.rb +199 -0
- data/lib/maruku/usage/example1.rb +33 -0
- data/lib/maruku/version.rb +39 -0
- metadata +167 -0
@@ -0,0 +1,111 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
3
|
+
#
|
4
|
+
# This file is part of Maruku.
|
5
|
+
#
|
6
|
+
# Maruku is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Maruku is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
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
|
+
|
22
|
+
module MaRuKu; module In; module Markdown; module BlockLevelParser
|
23
|
+
|
24
|
+
# This represents a source of lines that can be consumed.
|
25
|
+
#
|
26
|
+
# It is the twin of CharSource.
|
27
|
+
#
|
28
|
+
|
29
|
+
class LineSource
|
30
|
+
include MaRuKu::Strings
|
31
|
+
attr_reader :parent
|
32
|
+
|
33
|
+
def initialize(lines, parent=nil, parent_offset=nil)
|
34
|
+
raise "NIL lines? " if not lines
|
35
|
+
@lines = lines
|
36
|
+
@lines_index = 0
|
37
|
+
@parent = parent
|
38
|
+
@parent_offset = parent_offset
|
39
|
+
end
|
40
|
+
|
41
|
+
def cur_line() @lines[@lines_index] end
|
42
|
+
def next_line() @lines[@lines_index+1] end
|
43
|
+
|
44
|
+
def shift_line()
|
45
|
+
raise "Over the rainbow" if @lines_index >= @lines.size
|
46
|
+
l = @lines[@lines_index]
|
47
|
+
@lines_index += 1
|
48
|
+
return l
|
49
|
+
end
|
50
|
+
|
51
|
+
def ignore_line
|
52
|
+
raise "Over the rainbow" if @lines_index >= @lines.size
|
53
|
+
@lines_index += 1
|
54
|
+
end
|
55
|
+
|
56
|
+
def describe
|
57
|
+
s = "At line #{original_line_number(@lines_index)}\n"
|
58
|
+
|
59
|
+
context = 3 # lines
|
60
|
+
from = [@lines_index-context, 0].max
|
61
|
+
to = [@lines_index+context, @lines.size-1].min
|
62
|
+
|
63
|
+
for i in from..to
|
64
|
+
prefix = (i == @lines_index) ? '--> ' : ' ';
|
65
|
+
l = @lines[i]
|
66
|
+
s += "%10s %4s|%s" %
|
67
|
+
[@lines[i].md_type.to_s, prefix, l]
|
68
|
+
|
69
|
+
s += "|\n"
|
70
|
+
end
|
71
|
+
|
72
|
+
# if @parent
|
73
|
+
# s << "Parent context is: \n"
|
74
|
+
# s << add_tabs(@parent.describe,1,'|')
|
75
|
+
# end
|
76
|
+
s
|
77
|
+
end
|
78
|
+
|
79
|
+
def original_line_number(index)
|
80
|
+
if @parent
|
81
|
+
return index + @parent.original_line_number(@parent_offset)
|
82
|
+
else
|
83
|
+
1 + index
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def cur_index
|
88
|
+
@lines_index
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns the type of next line as a string
|
92
|
+
# breaks at first :definition
|
93
|
+
def tell_me_the_future
|
94
|
+
s = ""; num_e = 0;
|
95
|
+
for i in @lines_index..@lines.size-1
|
96
|
+
c = case @lines[i].md_type
|
97
|
+
when :text; "t"
|
98
|
+
when :empty; num_e+=1; "e"
|
99
|
+
when :definition; "d"
|
100
|
+
else "o"
|
101
|
+
end
|
102
|
+
s += c
|
103
|
+
break if c == "d" or num_e>1
|
104
|
+
end
|
105
|
+
s
|
106
|
+
end
|
107
|
+
|
108
|
+
end # linesource
|
109
|
+
|
110
|
+
end end end end # block
|
111
|
+
|
@@ -0,0 +1,616 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (C) 2006 Andrea Censi <andrea (at) rubyforge.org>
|
3
|
+
#
|
4
|
+
# This file is part of Maruku.
|
5
|
+
#
|
6
|
+
# Maruku is free software; you can redistribute it and/or modify
|
7
|
+
# it under the terms of the GNU General Public License as published by
|
8
|
+
# the Free Software Foundation; either version 2 of the License, or
|
9
|
+
# (at your option) any later version.
|
10
|
+
#
|
11
|
+
# Maruku is distributed in the hope that it will be useful,
|
12
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
13
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
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
|
+
|
22
|
+
module MaRuKu; module In; module Markdown; module BlockLevelParser
|
23
|
+
|
24
|
+
include Helpers
|
25
|
+
include MaRuKu::Strings
|
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
|
36
|
+
|
37
|
+
# Splits the string and calls parse_lines_as_markdown
|
38
|
+
def parse_text_as_markdown(text)
|
39
|
+
lines = split_lines(text)
|
40
|
+
src = LineSource.new(lines)
|
41
|
+
return parse_blocks(src)
|
42
|
+
end
|
43
|
+
|
44
|
+
# Input is a LineSource
|
45
|
+
def parse_blocks(src)
|
46
|
+
output = BlockContext.new
|
47
|
+
|
48
|
+
# run state machine
|
49
|
+
while src.cur_line
|
50
|
+
|
51
|
+
next if check_block_extensions(src, output, src.cur_line)
|
52
|
+
|
53
|
+
# Prints detected type (useful for debugging)
|
54
|
+
# puts "#{src.cur_line.md_type}|#{src.cur_line}"
|
55
|
+
case src.cur_line.md_type
|
56
|
+
when :empty;
|
57
|
+
output.push :empty
|
58
|
+
src.ignore_line
|
59
|
+
when :ial
|
60
|
+
m = InlineAttributeList.match src.shift_line
|
61
|
+
content = m[1] || ""
|
62
|
+
# puts "Content: #{content.inspect}"
|
63
|
+
src2 = CharSource.new(content, src)
|
64
|
+
interpret_extension(src2, output, [nil])
|
65
|
+
when :ald
|
66
|
+
output.push read_ald(src)
|
67
|
+
when :text
|
68
|
+
# paragraph, or table, or definition list
|
69
|
+
read_text_material(src, output)
|
70
|
+
when :header2, :hrule
|
71
|
+
# hrule
|
72
|
+
src.shift_line
|
73
|
+
output.push md_hrule()
|
74
|
+
when :header3
|
75
|
+
output.push read_header3(src)
|
76
|
+
when :ulist, :olist
|
77
|
+
list_type = src.cur_line.md_type == :ulist ? :ul : :ol
|
78
|
+
li = read_list_item(src)
|
79
|
+
# append to current list if we have one
|
80
|
+
if output.last.kind_of?(MDElement) &&
|
81
|
+
output.last.node_type == list_type then
|
82
|
+
output.last.children << li
|
83
|
+
else
|
84
|
+
output.push md_el(list_type, [li])
|
85
|
+
end
|
86
|
+
when :quote; output.push read_quote(src)
|
87
|
+
when :code; e = read_code(src); output << e if e
|
88
|
+
when :raw_html; e = read_raw_html(src); output << e if e
|
89
|
+
|
90
|
+
when :footnote_text; output.push read_footnote_text(src)
|
91
|
+
when :ref_definition;
|
92
|
+
if src.parent && (src.cur_index == 0)
|
93
|
+
read_text_material(src, output)
|
94
|
+
else
|
95
|
+
read_ref_definition(src, output)
|
96
|
+
end
|
97
|
+
when :abbreviation; output.push read_abbreviation(src)
|
98
|
+
when :xml_instr; read_xml_instruction(src, output)
|
99
|
+
when :metadata;
|
100
|
+
maruku_error "Please use the new meta-data syntax: \n"+
|
101
|
+
" http://maruku.rubyforge.org/proposal.html\n", src
|
102
|
+
src.ignore_line
|
103
|
+
else # warn if we forgot something
|
104
|
+
md_type = src.cur_line.md_type
|
105
|
+
line = src.cur_line
|
106
|
+
maruku_error "Ignoring line '#{line}' type = #{md_type}", src
|
107
|
+
src.shift_line
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
merge_ial(output, src, output)
|
112
|
+
output.delete_if {|x| x.kind_of?(MDElement) &&
|
113
|
+
x.node_type == :ial}
|
114
|
+
|
115
|
+
# get rid of empty line markers
|
116
|
+
output.delete_if {|x| x == :empty}
|
117
|
+
# See for each list if we can omit the paragraphs and use li_span
|
118
|
+
# TODO: do this after
|
119
|
+
output.each do |c|
|
120
|
+
# Remove paragraphs that we can get rid of
|
121
|
+
if [:ul,:ol].include? c.node_type
|
122
|
+
if c.children.all? {|li| !li.want_my_paragraph} then
|
123
|
+
c.children.each do |d|
|
124
|
+
d.node_type = :li_span
|
125
|
+
d.children = d.children[0].children
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
if c.node_type == :definition_list
|
130
|
+
if c.children.all?{|defi| !defi.want_my_paragraph} then
|
131
|
+
c.children.each do |definition|
|
132
|
+
definition.definitions.each do |dd|
|
133
|
+
dd.children = dd.children[0].children
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
output
|
141
|
+
end
|
142
|
+
|
143
|
+
def read_text_material(src, output)
|
144
|
+
if src.cur_line =~ MightBeTableHeader and
|
145
|
+
(src.next_line && src.next_line =~ TableSeparator)
|
146
|
+
output.push read_table(src)
|
147
|
+
elsif [:header1,:header2].include? src.next_line.md_type
|
148
|
+
output.push read_header12(src)
|
149
|
+
elsif eventually_comes_a_def_list(src)
|
150
|
+
definition = read_definition(src)
|
151
|
+
if output.last.kind_of?(MDElement) &&
|
152
|
+
output.last.node_type == :definition_list then
|
153
|
+
output.last.children << definition
|
154
|
+
else
|
155
|
+
output.push md_el(:definition_list, [definition])
|
156
|
+
end
|
157
|
+
else # Start of a paragraph
|
158
|
+
output.push read_paragraph(src)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
|
163
|
+
def read_ald(src)
|
164
|
+
if (l=src.shift_line) =~ AttributeDefinitionList
|
165
|
+
id = $1; al=$2;
|
166
|
+
al = read_attribute_list(CharSource.new(al,src), context=nil, break_on=[nil])
|
167
|
+
self.ald[id] = al;
|
168
|
+
return md_ald(id, al)
|
169
|
+
else
|
170
|
+
maruku_error "Bug Bug:\n#{l.inspect}"
|
171
|
+
return nil
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
# reads a header (with ----- or ========)
|
176
|
+
def read_header12(src)
|
177
|
+
line = src.shift_line.strip
|
178
|
+
al = nil
|
179
|
+
# Check if there is an IAL
|
180
|
+
if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
|
181
|
+
line = $1.strip
|
182
|
+
ial = $2
|
183
|
+
al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
|
184
|
+
end
|
185
|
+
text = parse_lines_as_span [ line ]
|
186
|
+
level = src.cur_line.md_type == :header2 ? 2 : 1;
|
187
|
+
src.shift_line
|
188
|
+
return md_header(level, text, al)
|
189
|
+
end
|
190
|
+
|
191
|
+
# reads a header like '#### header ####'
|
192
|
+
def read_header3(src)
|
193
|
+
line = src.shift_line.strip
|
194
|
+
al = nil
|
195
|
+
# Check if there is an IAL
|
196
|
+
if new_meta_data? and line =~ /^(.*)\{(.*)\}\s*$/
|
197
|
+
line = $1.strip
|
198
|
+
ial = $2
|
199
|
+
al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil])
|
200
|
+
end
|
201
|
+
level = num_leading_hashes(line)
|
202
|
+
text = parse_lines_as_span [strip_hashes(line)]
|
203
|
+
return md_header(level, text, al)
|
204
|
+
end
|
205
|
+
|
206
|
+
def read_xml_instruction(src, output)
|
207
|
+
m = /^\s*<\?((\w+)\s*)?(.*)$/.match src.shift_line
|
208
|
+
raise "BugBug" if not m
|
209
|
+
target = m[2] || ''
|
210
|
+
code = m[3]
|
211
|
+
until code =~ /\?>/
|
212
|
+
code += "\n"+src.shift_line
|
213
|
+
end
|
214
|
+
if not code =~ (/\?>\s*$/)
|
215
|
+
garbage = (/\?>(.*)$/.match(code))[1]
|
216
|
+
maruku_error "Trailing garbage on last line: #{garbage.inspect}:\n"+
|
217
|
+
add_tabs(code, 1, '|'), src
|
218
|
+
end
|
219
|
+
code.gsub!(/\?>\s*$/, '')
|
220
|
+
|
221
|
+
if target == 'mrk' && MaRuKu::Globals[:unsafe_features]
|
222
|
+
result = safe_execute_code(self, code)
|
223
|
+
if result
|
224
|
+
if result.kind_of? String
|
225
|
+
raise "Not expected"
|
226
|
+
else
|
227
|
+
output.push(*result)
|
228
|
+
end
|
229
|
+
end
|
230
|
+
else
|
231
|
+
output.push md_xml_instr(target, code)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def read_raw_html(src)
|
236
|
+
h = HTMLHelper.new
|
237
|
+
begin
|
238
|
+
h.eat_this(l=src.shift_line)
|
239
|
+
# puts "\nBLOCK:\nhtml -> #{l.inspect}"
|
240
|
+
while src.cur_line and not h.is_finished?
|
241
|
+
l=src.shift_line
|
242
|
+
# puts "html -> #{l.inspect}"
|
243
|
+
h.eat_this "\n"+l
|
244
|
+
end
|
245
|
+
rescue Exception => e
|
246
|
+
ex = e.inspect + e.backtrace.join("\n")
|
247
|
+
maruku_error "Bad block-level HTML:\n#{add_tabs(ex,1,'|')}\n", src
|
248
|
+
end
|
249
|
+
if not (h.rest =~ /^\s*$/)
|
250
|
+
maruku_error "Could you please format this better?\n"+
|
251
|
+
"I see that #{h.rest.inspect} is left after the raw HTML.", src
|
252
|
+
end
|
253
|
+
raw_html = h.stuff_you_read
|
254
|
+
|
255
|
+
return md_html(raw_html)
|
256
|
+
end
|
257
|
+
|
258
|
+
def read_paragraph(src)
|
259
|
+
lines = [src.shift_line]
|
260
|
+
while src.cur_line
|
261
|
+
# :olist does not break
|
262
|
+
case t = src.cur_line.md_type
|
263
|
+
when :quote,:header3,:empty,:ref_definition,:ial #,:xml_instr,:raw_html
|
264
|
+
break
|
265
|
+
when :olist,:ulist
|
266
|
+
break if src.next_line.md_type == t
|
267
|
+
end
|
268
|
+
break if src.cur_line.strip.size == 0
|
269
|
+
break if [:header1,:header2].include? src.next_line.md_type
|
270
|
+
break if any_matching_block_extension?(src.cur_line)
|
271
|
+
|
272
|
+
lines << src.shift_line
|
273
|
+
end
|
274
|
+
# dbg_describe_ary(lines, 'PAR')
|
275
|
+
children = parse_lines_as_span(lines, src)
|
276
|
+
|
277
|
+
return md_par(children)
|
278
|
+
end
|
279
|
+
|
280
|
+
# Reads one list item, either ordered or unordered.
|
281
|
+
def read_list_item(src)
|
282
|
+
parent_offset = src.cur_index
|
283
|
+
|
284
|
+
item_type = src.cur_line.md_type
|
285
|
+
first = src.shift_line
|
286
|
+
|
287
|
+
indentation, ial = spaces_before_first_char(first)
|
288
|
+
al = read_attribute_list(CharSource.new(ial,src), context=nil, break_on=[nil]) if ial
|
289
|
+
break_list = [:ulist, :olist, :ial]
|
290
|
+
# Ugly things going on inside `read_indented_content`
|
291
|
+
lines, want_my_paragraph =
|
292
|
+
read_indented_content(src,indentation, break_list, item_type)
|
293
|
+
|
294
|
+
# add first line
|
295
|
+
# Strip first '*', '-', '+' from first line
|
296
|
+
stripped = first[indentation, first.size-1]
|
297
|
+
lines.unshift stripped
|
298
|
+
|
299
|
+
# dbg_describe_ary(lines, 'LIST ITEM ')
|
300
|
+
|
301
|
+
src2 = LineSource.new(lines, src, parent_offset)
|
302
|
+
children = parse_blocks(src2)
|
303
|
+
with_par = want_my_paragraph || (children.size>1)
|
304
|
+
|
305
|
+
return md_li(children, with_par, al)
|
306
|
+
end
|
307
|
+
|
308
|
+
def read_abbreviation(src)
|
309
|
+
if not (l=src.shift_line) =~ Abbreviation
|
310
|
+
maruku_error "Bug: it's Andrea's fault. Tell him.\n#{l.inspect}"
|
311
|
+
end
|
312
|
+
|
313
|
+
abbr = $1
|
314
|
+
desc = $2
|
315
|
+
|
316
|
+
if (not abbr) or (abbr.size==0)
|
317
|
+
maruku_error "Bad abbrev. abbr=#{abbr.inspect} desc=#{desc.inspect}"
|
318
|
+
end
|
319
|
+
|
320
|
+
self.abbreviations[abbr] = desc
|
321
|
+
|
322
|
+
return md_abbr_def(abbr, desc)
|
323
|
+
end
|
324
|
+
|
325
|
+
def read_footnote_text(src)
|
326
|
+
parent_offset = src.cur_index
|
327
|
+
|
328
|
+
first = src.shift_line
|
329
|
+
|
330
|
+
if not first =~ FootnoteText
|
331
|
+
maruku_error "Bug (it's Andrea's fault)"
|
332
|
+
end
|
333
|
+
|
334
|
+
id = $1
|
335
|
+
text = $2
|
336
|
+
|
337
|
+
# Ugly things going on inside `read_indented_content`
|
338
|
+
indentation = 4 #first.size-text.size
|
339
|
+
|
340
|
+
# puts "id =_#{id}_; text=_#{text}_ indent=#{indentation}"
|
341
|
+
|
342
|
+
break_list = [:footnote_text, :ref_definition, :definition, :abbreviation]
|
343
|
+
item_type = :footnote_text
|
344
|
+
lines, want_my_paragraph =
|
345
|
+
read_indented_content(src,indentation, break_list, item_type)
|
346
|
+
|
347
|
+
# add first line
|
348
|
+
if text && text.strip != "" then lines.unshift text end
|
349
|
+
|
350
|
+
# dbg_describe_ary(lines, 'FOOTNOTE')
|
351
|
+
src2 = LineSource.new(lines, src, parent_offset)
|
352
|
+
children = parse_blocks(src2)
|
353
|
+
|
354
|
+
e = md_footnote(id, children)
|
355
|
+
self.footnotes[id] = e
|
356
|
+
return e
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
# This is the only ugly function in the code base.
|
361
|
+
# It is used to read list items, descriptions, footnote text
|
362
|
+
def read_indented_content(src, indentation, break_list, item_type)
|
363
|
+
lines =[]
|
364
|
+
# collect all indented lines
|
365
|
+
saw_empty = false; saw_anything_after = false
|
366
|
+
while src.cur_line
|
367
|
+
# puts "Reading indent = #{indentation} #{src.cur_line.inspect}"
|
368
|
+
#puts "#{src.cur_line.md_type} #{src.cur_line.inspect}"
|
369
|
+
if src.cur_line.md_type == :empty
|
370
|
+
saw_empty = true
|
371
|
+
lines << src.shift_line
|
372
|
+
next
|
373
|
+
end
|
374
|
+
|
375
|
+
# after a white line
|
376
|
+
if saw_empty
|
377
|
+
# we expect things to be properly aligned
|
378
|
+
if (ns=number_of_leading_spaces(src.cur_line)) < indentation
|
379
|
+
#puts "breaking for spaces, only #{ns}: #{src.cur_line}"
|
380
|
+
break
|
381
|
+
end
|
382
|
+
saw_anything_after = true
|
383
|
+
else
|
384
|
+
# if src.cur_line[0] != ?\
|
385
|
+
break if break_list.include? src.cur_line.md_type
|
386
|
+
# end
|
387
|
+
# break if src.cur_line.md_type != :text
|
388
|
+
end
|
389
|
+
|
390
|
+
|
391
|
+
stripped = strip_indent(src.shift_line, indentation)
|
392
|
+
lines << stripped
|
393
|
+
|
394
|
+
#puts "Accepted as #{stripped.inspect}"
|
395
|
+
|
396
|
+
# You are only required to indent the first line of
|
397
|
+
# a child paragraph.
|
398
|
+
if stripped.md_type == :text
|
399
|
+
while src.cur_line && (src.cur_line.md_type == :text)
|
400
|
+
lines << strip_indent(src.shift_line, indentation)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
want_my_paragraph = saw_anything_after ||
|
406
|
+
(saw_empty && (src.cur_line && (src.cur_line.md_type == item_type)))
|
407
|
+
|
408
|
+
# dbg_describe_ary(lines, 'LI')
|
409
|
+
# create a new context
|
410
|
+
|
411
|
+
while lines.last && (lines.last.md_type == :empty)
|
412
|
+
lines.pop
|
413
|
+
end
|
414
|
+
|
415
|
+
return lines, want_my_paragraph
|
416
|
+
end
|
417
|
+
|
418
|
+
|
419
|
+
def read_quote(src)
|
420
|
+
parent_offset = src.cur_index
|
421
|
+
|
422
|
+
lines = []
|
423
|
+
# collect all indented lines
|
424
|
+
while src.cur_line && src.cur_line.md_type == :quote
|
425
|
+
lines << unquote(src.shift_line)
|
426
|
+
end
|
427
|
+
# dbg_describe_ary(lines, 'QUOTE')
|
428
|
+
|
429
|
+
src2 = LineSource.new(lines, src, parent_offset)
|
430
|
+
children = parse_blocks(src2)
|
431
|
+
return md_quote(children)
|
432
|
+
end
|
433
|
+
|
434
|
+
def read_code(src)
|
435
|
+
# collect all indented lines
|
436
|
+
lines = []
|
437
|
+
while src.cur_line && ([:code, :empty].include? src.cur_line.md_type)
|
438
|
+
lines << strip_indent(src.shift_line, 4)
|
439
|
+
end
|
440
|
+
|
441
|
+
#while lines.last && (lines.last.md_type == :empty )
|
442
|
+
while lines.last && lines.last.strip.size == 0
|
443
|
+
lines.pop
|
444
|
+
end
|
445
|
+
|
446
|
+
while lines.first && lines.first.strip.size == 0
|
447
|
+
lines.shift
|
448
|
+
end
|
449
|
+
|
450
|
+
return nil if lines.empty?
|
451
|
+
|
452
|
+
source = lines.join("\n")
|
453
|
+
|
454
|
+
# dbg_describe_ary(lines, 'CODE')
|
455
|
+
|
456
|
+
return md_codeblock(source)
|
457
|
+
end
|
458
|
+
|
459
|
+
# Reads a series of metadata lines with empty lines in between
|
460
|
+
def read_metadata(src)
|
461
|
+
hash = {}
|
462
|
+
while src.cur_line
|
463
|
+
case src.cur_line.md_type
|
464
|
+
when :empty; src.shift_line
|
465
|
+
when :metadata; hash.merge! parse_metadata(src.shift_line)
|
466
|
+
else break
|
467
|
+
end
|
468
|
+
end
|
469
|
+
hash
|
470
|
+
end
|
471
|
+
|
472
|
+
|
473
|
+
def read_ref_definition(src, out)
|
474
|
+
line = src.shift_line
|
475
|
+
|
476
|
+
|
477
|
+
# if link is incomplete, shift next line
|
478
|
+
if src.cur_line && !([:footnote_text, :ref_definition, :definition, :abbreviation].include? src.cur_line.md_type) &&
|
479
|
+
([1,2,3].include? number_of_leading_spaces(src.cur_line) )
|
480
|
+
line += " "+ src.shift_line
|
481
|
+
end
|
482
|
+
|
483
|
+
# puts "total= #{line}"
|
484
|
+
|
485
|
+
match = LinkRegex.match(line)
|
486
|
+
if not match
|
487
|
+
maruku_error "Link does not respect format: '#{line}'"
|
488
|
+
return
|
489
|
+
end
|
490
|
+
|
491
|
+
id = match[1]; url = match[2]; title = match[3];
|
492
|
+
id = sanitize_ref_id(id)
|
493
|
+
|
494
|
+
hash = self.refs[id] = {:url=>url,:title=>title}
|
495
|
+
|
496
|
+
stuff=match[4]
|
497
|
+
|
498
|
+
if stuff
|
499
|
+
stuff.split.each do |couple|
|
500
|
+
# puts "found #{couple}"
|
501
|
+
k, v = couple.split('=')
|
502
|
+
v ||= ""
|
503
|
+
if v[0,1]=='"' then v = v[1, v.size-2] end
|
504
|
+
# puts "key:_#{k}_ value=_#{v}_"
|
505
|
+
hash[k.to_sym] = v
|
506
|
+
end
|
507
|
+
end
|
508
|
+
# puts hash.inspect
|
509
|
+
|
510
|
+
out.push md_ref_def(id, url, meta={:title=>title})
|
511
|
+
end
|
512
|
+
|
513
|
+
def split_cells(s)
|
514
|
+
# s.strip.split('|').select{|x|x.strip.size>0}.map{|x|x.strip}
|
515
|
+
# changed to allow empty cells
|
516
|
+
s.strip.split('|').select{|x|x.size>0}.map{|x|x.strip}
|
517
|
+
end
|
518
|
+
|
519
|
+
def read_table(src)
|
520
|
+
head = split_cells(src.shift_line).map{|s| md_el(:head_cell, parse_lines_as_span([s])) }
|
521
|
+
|
522
|
+
separator=split_cells(src.shift_line)
|
523
|
+
|
524
|
+
align = separator.map { |s| s =~ Sep
|
525
|
+
if $1 and $2 then :center elsif $2 then :right else :left end }
|
526
|
+
|
527
|
+
num_columns = align.size
|
528
|
+
|
529
|
+
if head.size != num_columns
|
530
|
+
maruku_error "Table head does not have #{num_columns} columns: \n#{head.inspect}"
|
531
|
+
tell_user "I will ignore this table."
|
532
|
+
# XXX try to recover
|
533
|
+
return md_br()
|
534
|
+
end
|
535
|
+
|
536
|
+
rows = []
|
537
|
+
|
538
|
+
while src.cur_line && src.cur_line =~ /\|/
|
539
|
+
row = split_cells(src.shift_line).map{|s|
|
540
|
+
md_el(:cell, parse_lines_as_span([s]))}
|
541
|
+
if head.size != num_columns
|
542
|
+
maruku_error "Row does not have #{num_columns} columns: \n#{row.inspect}"
|
543
|
+
tell_user "I will ignore this table."
|
544
|
+
# XXX try to recover
|
545
|
+
return md_br()
|
546
|
+
end
|
547
|
+
rows << row
|
548
|
+
end
|
549
|
+
|
550
|
+
children = (head+rows).flatten
|
551
|
+
return md_el(:table, children, {:align => align})
|
552
|
+
end
|
553
|
+
|
554
|
+
# If current line is text, a definition list is coming
|
555
|
+
# if 1) text,empty,[text,empty]*,definition
|
556
|
+
|
557
|
+
def eventually_comes_a_def_list(src)
|
558
|
+
future = src.tell_me_the_future
|
559
|
+
ok = future =~ %r{^t+e?d}x
|
560
|
+
# puts "future: #{future} - #{ok}"
|
561
|
+
ok
|
562
|
+
end
|
563
|
+
|
564
|
+
|
565
|
+
def read_definition(src)
|
566
|
+
# Read one or more terms
|
567
|
+
terms = []
|
568
|
+
while src.cur_line && src.cur_line.md_type == :text
|
569
|
+
terms << md_el(:definition_term, parse_lines_as_span([src.shift_line]))
|
570
|
+
end
|
571
|
+
# dbg_describe_ary(terms, 'DT')
|
572
|
+
|
573
|
+
want_my_paragraph = false
|
574
|
+
|
575
|
+
raise "Chunky Bacon!" if not src.cur_line
|
576
|
+
|
577
|
+
# one optional empty
|
578
|
+
if src.cur_line.md_type == :empty
|
579
|
+
want_my_paragraph = true
|
580
|
+
src.shift_line
|
581
|
+
end
|
582
|
+
|
583
|
+
raise "Chunky Bacon!" if src.cur_line.md_type != :definition
|
584
|
+
|
585
|
+
# Read one or more definitions
|
586
|
+
definitions = []
|
587
|
+
while src.cur_line && src.cur_line.md_type == :definition
|
588
|
+
parent_offset = src.cur_index
|
589
|
+
|
590
|
+
first = src.shift_line
|
591
|
+
first =~ Definition
|
592
|
+
first = $1
|
593
|
+
|
594
|
+
# I know, it's ugly!!!
|
595
|
+
|
596
|
+
lines, w_m_p =
|
597
|
+
read_indented_content(src,4, [:definition], :definition)
|
598
|
+
want_my_paragraph ||= w_m_p
|
599
|
+
|
600
|
+
lines.unshift first
|
601
|
+
|
602
|
+
# dbg_describe_ary(lines, 'DD')
|
603
|
+
src2 = LineSource.new(lines, src, parent_offset)
|
604
|
+
children = parse_blocks(src2)
|
605
|
+
definitions << md_el(:definition_data, children)
|
606
|
+
end
|
607
|
+
|
608
|
+
return md_el(:definition, terms+definitions, {
|
609
|
+
:terms => terms,
|
610
|
+
:definitions => definitions,
|
611
|
+
:want_my_paragraph => want_my_paragraph})
|
612
|
+
end
|
613
|
+
end # BlockLevelParser
|
614
|
+
end # MaRuKu
|
615
|
+
end
|
616
|
+
end
|