trac-wiki 0.2.24 → 0.3.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +0 -2
- data/Gemfile.lock +1 -9
- data/README +92 -11
- data/lib/trac-wiki/env.rb +92 -63
- data/lib/trac-wiki/parser.rb +248 -254
- data/lib/trac-wiki/tree.rb +22 -27
- data/lib/trac-wiki/version.rb +1 -1
- data/lib/trac-wiki.rb +4 -4
- data/test/parser_test.rb +112 -33
- metadata +2 -2
data/lib/trac-wiki/parser.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'cgi'
|
2
3
|
require 'uri'
|
3
|
-
require 'iconv'
|
4
4
|
require 'yaml'
|
5
|
+
require 'unicode_utils/compatibility_decomposition'
|
5
6
|
|
6
7
|
# :main: TracWiki
|
7
8
|
|
8
9
|
# The TracWiki parses and translates Trac formatted text into
|
9
|
-
# XHTML.
|
10
|
+
# XHTML. TracWiki is a lightweight markup syntax similar to what many
|
10
11
|
# WikiWikiWebs use. Example syntax:
|
11
12
|
#
|
12
13
|
# = Heading 1 =
|
@@ -20,12 +21,7 @@ require 'yaml'
|
|
20
21
|
# [[Image(image.png)]]
|
21
22
|
# [[Image(image.png, options)]]
|
22
23
|
#
|
23
|
-
#
|
24
|
-
# links allow explicit local links using the [[link]] syntax. External
|
25
|
-
# links will only be allowed if specified using http(s) and ftp(s)
|
26
|
-
# schemes. If special link handling is needed, such as inter-wiki or
|
27
|
-
# hierachical local links, you must inherit Creole::CreoleParser and
|
28
|
-
# override make_local_link.
|
24
|
+
# for more see http://trac.edgewall.org/wiki/WikiFormatting
|
29
25
|
#
|
30
26
|
# You can customize the created image markup by overriding
|
31
27
|
# make_image.
|
@@ -48,7 +44,19 @@ module TracWiki
|
|
48
44
|
# Examples: http https ftp ftps
|
49
45
|
attr_accessor :allowed_schemes
|
50
46
|
|
47
|
+
# structure where headings are stroed
|
48
|
+
# list of hasheses with `level` and `title`, `sline`
|
49
|
+
# [ { leven: 1, # <h1>
|
50
|
+
# sline: 3, # line where head starts
|
51
|
+
# eline: 4, # line before next heading starts
|
52
|
+
# aname: 'anchor-to-this-heading',
|
53
|
+
# title: 'heading title'
|
54
|
+
# },
|
55
|
+
# ...
|
56
|
+
# ]
|
51
57
|
attr_accessor :headings
|
58
|
+
|
59
|
+
# url base for links
|
52
60
|
attr_writer :base
|
53
61
|
|
54
62
|
# Disable url escaping for local links
|
@@ -62,14 +70,19 @@ module TracWiki
|
|
62
70
|
attr_writer :no_link
|
63
71
|
def no_link?; @no_link; end
|
64
72
|
|
73
|
+
# math syntax extension:
|
74
|
+
# $e^x$ for inline math
|
75
|
+
# $$ e^x $$ for display math
|
65
76
|
attr_writer :math
|
66
77
|
def math?; @math; end
|
67
78
|
|
68
79
|
# allow some <b> <form> <html>
|
69
80
|
# html will be sanitized
|
70
|
-
attr_writer :
|
71
|
-
def
|
81
|
+
attr_writer :allow_html
|
82
|
+
def allow_html?; @allow_html; end
|
72
83
|
|
84
|
+
# add '<a class='editheading' href="?edit=N>edit</a>'
|
85
|
+
# to each heading
|
73
86
|
attr_writer :edit_heading
|
74
87
|
def edit_heading?; @edit_heading; end
|
75
88
|
|
@@ -82,44 +95,94 @@ module TracWiki
|
|
82
95
|
attr_writer :merge
|
83
96
|
def merge?; @merge; end
|
84
97
|
|
85
|
-
# every heading
|
98
|
+
# every heading had id, generated from heading text
|
86
99
|
attr_writer :id_from_heading
|
87
100
|
def id_from_heading?; @id_from_heading; end
|
88
101
|
|
102
|
+
# use macros? defalut yes
|
103
|
+
attr_writer :macros
|
104
|
+
def macros?; @macros; end
|
105
|
+
|
89
106
|
# when id_from_heading, non ascii char are transliterated to ascii
|
90
107
|
attr_writer :id_translit
|
91
|
-
|
92
|
-
|
108
|
+
def id_translit?; @id_translit; end
|
109
|
+
|
110
|
+
# like template but more powerfull
|
111
|
+
# do no use.
|
112
|
+
attr_accessor :macro_commands
|
113
|
+
@macro_commands = {}
|
114
|
+
|
115
|
+
# template_handler(macroname) -> template_text
|
116
|
+
# when macros enabled and {{myCoolMacro}} ocured,
|
117
|
+
# result fo `template_handler('myCoolMacro') inserted
|
118
|
+
attr_accessor :template_handler
|
93
119
|
|
94
120
|
# string begins with macro
|
95
121
|
MACRO_BEG_REX = /\A\{\{ ( \$[\$\.\w]+ | [\#!]\w* |\w+ ) /x
|
96
122
|
MACRO_BEG_INSIDE_REX = /\A(.*?)(?<!\{)\{\{ ( \$[\$\.\w]+ | [\#!]\w* | \w+ ) /xm
|
97
123
|
# find end of marcro or begin of inner macro
|
98
124
|
MACRO_END_REX = /\A(.*?) ( \}\} | \{\{ ( \$[\$\.\w]+ | [\#!]\w* | \w+) )/mx
|
99
|
-
def id_translit?; @id_translit; end
|
100
125
|
|
101
126
|
# Create a new Parser instance.
|
102
|
-
def initialize(
|
103
|
-
|
127
|
+
def initialize(options = nil)
|
128
|
+
init_macros
|
129
|
+
@macros = true
|
104
130
|
@allowed_schemes = %w(http https ftp ftps)
|
105
|
-
|
106
|
-
|
107
|
-
@plugins.merge! plugins if ! plugins.nil?
|
108
|
-
@text = text
|
131
|
+
macro_commands = options.delete :macro_commands
|
132
|
+
@macro_commands.merge! macro_commands if ! macro_commands.nil?
|
109
133
|
@no_escape = nil
|
110
134
|
@base = ''
|
111
135
|
options.each_pair {|k,v| send("#{k}=", v) }
|
112
136
|
@base += '/' if !@base.empty? && @base[-1] != '/'
|
113
137
|
end
|
114
138
|
|
115
|
-
def
|
116
|
-
@
|
139
|
+
def text(text)
|
140
|
+
@text = text
|
141
|
+
return self
|
142
|
+
end
|
143
|
+
|
144
|
+
def was_math?; @was_math; end
|
145
|
+
|
146
|
+
def to_html(text = nil)
|
147
|
+
text(text) if ! text.nil?
|
148
|
+
@was_math = false
|
149
|
+
@anames = {}
|
150
|
+
@count_lines_level = 0
|
151
|
+
@text = text if !text.nil?
|
152
|
+
@tree = TracWiki::Tree.new
|
153
|
+
@edit_heading_class = 'editheading'
|
154
|
+
@headings = [ {level: 0, sline: 1 } ]
|
155
|
+
@p = false
|
156
|
+
@stack = []
|
157
|
+
@stacki = []
|
158
|
+
@was_math = false
|
159
|
+
@line_no = 1
|
160
|
+
parse_block(@text)
|
161
|
+
@tree.to_html
|
162
|
+
end
|
163
|
+
|
164
|
+
def make_toc_html
|
165
|
+
@tree = TracWiki::Tree.new
|
166
|
+
parse_block(make_toc)
|
167
|
+
@tree.to_html
|
168
|
+
end
|
169
|
+
|
170
|
+
def add_macro_command(name, &block)
|
171
|
+
@macro_commands[name] = block
|
172
|
+
end
|
173
|
+
|
174
|
+
protected
|
175
|
+
|
176
|
+
def add_line_no(count)
|
177
|
+
@line_no += count if @count_lines_level == 0
|
178
|
+
end
|
179
|
+
|
180
|
+
def init_macros
|
181
|
+
@macro_commands = {
|
117
182
|
'!ifeq' => proc { |env| env.expand_arg(0) == env.expand_arg(1) ? env.expand_arg(2) : env.expand_arg(3) },
|
118
183
|
'!ifdef' => proc { |env| env.at(env.expand_arg(0), nil, false).nil? ? env.expand_arg(2) : env.expand_arg(1) },
|
119
184
|
'!set' => proc { |env| env[env.expand_arg(0)] = env.expand_arg(1); '' },
|
120
185
|
'!yset' => proc { |env| env[env.expand_arg(0)] = YAML.load(env.arg(1)); '' },
|
121
|
-
|
122
|
-
# '!html' => proc { |env| "\n{{{!\n#{env.arg(0)}\n}}}\n" },
|
123
186
|
'!sub' => proc { |env| pat = env.expand_arg(1)
|
124
187
|
pat = Regexp.new(pat[1..-2]) if pat =~ /\A\/.*\/\Z/
|
125
188
|
env.expand_arg(0).gsub(pat, env.expand_arg(2))
|
@@ -136,10 +199,11 @@ module TracWiki
|
|
136
199
|
set = set.keys.sort
|
137
200
|
elsif set.is_a?(Array)
|
138
201
|
set = (0 .. set.size-1)
|
202
|
+
elsif set.nil?
|
203
|
+
set = []
|
139
204
|
else
|
140
205
|
print "error top(#{top}), set#{set} #{set.class}\n"
|
141
|
-
|
142
|
-
raise "Error in {{!for #{i_name}|#{top}|#{tmpl}}}"
|
206
|
+
raise "Error in {{!for #{i_name}|#{top}|#{tmpl}}} $#{top}.class=#{set.class}(#{set.to_s})"
|
143
207
|
end
|
144
208
|
end
|
145
209
|
set.map do |i|
|
@@ -151,52 +215,9 @@ module TracWiki
|
|
151
215
|
|
152
216
|
end
|
153
217
|
|
154
|
-
# th(macroname) -> template_text
|
155
|
-
attr_accessor :template_handler
|
156
|
-
|
157
|
-
@was_math = false
|
158
|
-
def was_math?; @was_math; end
|
159
|
-
|
160
|
-
# Convert CCreole text to HTML and return
|
161
|
-
# the result. The resulting HTML does not contain <html> and
|
162
|
-
# <body> tags.
|
163
|
-
#
|
164
|
-
# Example:
|
165
|
-
#
|
166
|
-
# parser = Parser.new("**Hello //World//**")
|
167
|
-
# parser.to_html
|
168
|
-
# #=> "<p><strong>Hello <em>World</em></strong></p>"
|
169
|
-
def to_html
|
170
|
-
@tree = TracWiki::Tree.new
|
171
|
-
@edit_heading_class = 'editheading'
|
172
|
-
@headings = [ {level: 0, sline: 1 } ]
|
173
|
-
@p = false
|
174
|
-
@stack = []
|
175
|
-
@stacki = []
|
176
|
-
@was_math = false
|
177
|
-
@line_no = 1
|
178
|
-
parse_block(@text)
|
179
|
-
@tree.to_html
|
180
|
-
end
|
181
|
-
|
182
|
-
def make_toc_html
|
183
|
-
@tree = TracWiki::Tree.new
|
184
|
-
parse_block(make_toc)
|
185
|
-
@tree.to_html
|
186
|
-
end
|
187
|
-
|
188
|
-
def add_plugin(name, &block)
|
189
|
-
@plugins[name] = block
|
190
|
-
end
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
protected
|
195
|
-
|
196
218
|
# Escape any characters with special meaning in HTML using HTML
|
197
219
|
# entities. (&<>" not ')
|
198
220
|
def escape_html(string)
|
199
|
-
#CGI::escapeHTML(string)
|
200
221
|
Parser.escapeHTML(string)
|
201
222
|
end
|
202
223
|
|
@@ -252,10 +273,10 @@ module TracWiki
|
|
252
273
|
@p = false
|
253
274
|
end
|
254
275
|
|
255
|
-
def start_paragraph
|
276
|
+
def start_paragraph(add_spc = true)
|
256
277
|
if @p
|
257
278
|
#FIXME: multiple space s
|
258
|
-
@tree.add_spc
|
279
|
+
@tree.add_spc if add_spc
|
259
280
|
else
|
260
281
|
end_paragraph
|
261
282
|
start_tag('p')
|
@@ -294,44 +315,12 @@ module TracWiki
|
|
294
315
|
"#{@base}#{escape_url(link)}##{escape_url(anch)}"
|
295
316
|
end
|
296
317
|
|
297
|
-
# Sanatize a direct url (e.g. http://wikipedia.org/). The default
|
298
|
-
# behaviour returns the original link as-is.
|
299
|
-
#
|
300
|
-
# Must ensure that the result is properly URL-escaped. The caller
|
301
|
-
# will handle HTML escaping as necessary. Links will not be
|
302
|
-
# converted to HTML links if the function returns link.
|
303
|
-
#
|
304
|
-
# Custom versions of this function in inherited classes can
|
305
|
-
# implement specific link handling behaviour, such as redirection
|
306
|
-
# to intermediate pages (for example, for notifing the user that
|
307
|
-
# he is leaving the site).
|
308
|
-
def make_direct_link(url) #:doc:
|
309
|
-
url
|
310
|
-
end
|
311
|
-
|
312
|
-
# Sanatize and prefix image URLs. When images are encountered in
|
313
|
-
# Creole text, this function is called to obtain the actual URL of
|
314
|
-
# the image. The default behaviour is to return the image link
|
315
|
-
# as-is. No image tags are inserted if the function returns nil.
|
316
|
-
#
|
317
|
-
# Custom version of the method can be used to sanatize URLs
|
318
|
-
# (e.g. remove query-parts), inhibit off-site images, or add a
|
319
|
-
# base URL, for example:
|
320
|
-
#
|
321
|
-
# def make_image_link(url)
|
322
|
-
# URI.join("http://mywiki.org/images/", url)
|
323
|
-
# end
|
324
|
-
def make_image_link(url) #:doc:
|
325
|
-
url
|
326
|
-
end
|
327
|
-
|
328
318
|
# Create image markup. This
|
329
319
|
# method can be overridden to generate custom
|
330
320
|
# markup, for example to add html additional attributes or
|
331
321
|
# to put divs around the imgs.
|
332
322
|
def make_image(uri, attrs='')
|
333
|
-
|
334
|
-
@tree.tag(:img, make_image_attrs(uri, attrs))
|
323
|
+
@tree.tag(:img, make_image_attrs(@base + uri, attrs))
|
335
324
|
end
|
336
325
|
|
337
326
|
def make_image_attrs(uri, attrs)
|
@@ -361,12 +350,12 @@ module TracWiki
|
|
361
350
|
return a;
|
362
351
|
end
|
363
352
|
|
364
|
-
def make_headline(level,
|
353
|
+
def make_headline(level, title, aname, title_offset)
|
365
354
|
|
366
355
|
hN = "h#{level}".to_sym
|
367
356
|
|
368
357
|
@tree.tag_beg(hN, { id: aname } )
|
369
|
-
parse_inline(
|
358
|
+
parse_inline(title, title_offset)
|
370
359
|
if edit_heading?
|
371
360
|
edit_heading_link(@headings.size - 1)
|
372
361
|
end
|
@@ -398,148 +387,137 @@ module TracWiki
|
|
398
387
|
end.join
|
399
388
|
end
|
400
389
|
|
401
|
-
def parse_inline(str)
|
390
|
+
def parse_inline(str, offset)
|
391
|
+
raise "offset is nil" if offset.nil?
|
402
392
|
until str.empty?
|
403
|
-
case
|
404
|
-
# raw url
|
405
|
-
when /\A(!)?((https?|ftps?):\/\/\S+?)(?=([\]\,.?!:;"'\)]+)?(\s|$))/
|
406
|
-
|
407
|
-
|
408
|
-
@tree.add($2)
|
409
|
-
else
|
410
|
-
if uri = make_direct_link($2)
|
411
|
-
@tree.tag(:a, {href:uri}, $2)
|
412
|
-
else
|
413
|
-
@tree.add($&)
|
414
|
-
end
|
415
|
-
end
|
393
|
+
case
|
394
|
+
# raw url http://example.com/
|
395
|
+
when str =~ /\A(!)?((https?|ftps?):\/\/\S+?)(?=([\]\,.?!:;"'\)]+)?(\s|$))/
|
396
|
+
notlink, link = $1, $2
|
397
|
+
make_link(link, nil, link, 0, !!notlink)
|
416
398
|
# [[Image(pic.jpg|tag)]]
|
417
|
-
when /\A\[\[Image\(([^,]*?)(,(.*?))?\)\]\]/ # image
|
418
|
-
str = $'
|
399
|
+
when str =~ /\A\[\[Image\(([^,]*?)(,(.*?))?\)\]\]/ # image
|
419
400
|
make_image($1, $3)
|
420
401
|
# [[link]]
|
421
|
-
# [
|
422
|
-
when /\A \[ \s* ([^\[|]*?) \s* (\|\s*(.*?))? \s* \] /mx
|
423
|
-
|
424
|
-
link, content, whole
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
link, content, whole
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
@tree.
|
453
|
-
|
454
|
-
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
460
|
-
|
402
|
+
# [ link2 | text5 ]
|
403
|
+
when str =~ /\A (\[ \s* ([^\[|]*?) \s*) ((\|\s*)(.*?))? \s* \] /mx
|
404
|
+
link, content, content_offset, whole = $2, $5, $1.size + ($4||'').size, $&
|
405
|
+
make_link(link, content, "[#{whole}]",offset + content_offset)
|
406
|
+
# [[ link2 | text5 ]]
|
407
|
+
when str =~ /\A (\[\[ \s* ([^|]*?) \s*) ((\|\s*)(.*?))? \s* \]\] /mx
|
408
|
+
link, content, content_offset, whole= $2, $5, $1.size + ($4||'').size, $&
|
409
|
+
#print "link: #{content_offset} of:#{offset}, '#{$1}', '#{$4||''}'\n"
|
410
|
+
make_link(link, content, whole, offset + content_offset)
|
411
|
+
when allow_html? && str =~ /\A<(\/)?(\w+)(?:([^>]*?))?(\/\s*)?>/ # single inline <html> tag
|
412
|
+
eot, tag, args, closed = $1, $2, $3, $4
|
413
|
+
do_raw_tag(eot, tag, args, closed, $'.size)
|
414
|
+
when str =~ /\A\{\{\{(.*?\}*)\}\}\}/ # inline {{{ }}} pre (tt)
|
415
|
+
@tree.tag(:tt, $1)
|
416
|
+
when macros? && str =~ MACRO_BEG_REX # macro {{
|
417
|
+
mac, str, lines, offset = parse_macro($1, $', offset, $&.size)
|
418
|
+
parse_inline(mac.gsub(/\n/, ' '),0);
|
419
|
+
#print "MACRO.inline(#{$1}), next:#{str}"
|
420
|
+
#return str, offset
|
421
|
+
next
|
422
|
+
when str =~ /\A`(.*?)`/ # inline pre (tt)
|
423
|
+
@tree.tag(:tt, $1)
|
424
|
+
when math? && str =~ /\A\$(.+?)\$/ # inline math (tt)
|
425
|
+
@tree.tag(:span, {:class => 'math'}, $1)
|
426
|
+
@was_math = true
|
427
|
+
when str =~ /\A(\&\w*;)/ # html entity
|
428
|
+
#print "add html ent: #{$1}\n"
|
429
|
+
@tree.add_raw($1)
|
430
|
+
when str =~ /\A([:alpha:]|[:digit:])+/
|
431
|
+
@tree.add($&) # word
|
432
|
+
when str =~ /\A\s+/
|
433
|
+
@tree.add_spc
|
434
|
+
when str =~ /\A'''''/
|
435
|
+
toggle_tag 'strongem', $& # bolditallic
|
436
|
+
when str =~ /\A\*\*/ || str =~ /\A'''/
|
437
|
+
toggle_tag 'strong', $& # bold
|
438
|
+
when str =~ /\A''/ || str =~ /\A\/\//
|
439
|
+
toggle_tag 'em', $& # italic
|
440
|
+
when str =~ /\A\\\\/ || str =~ /\A\[\[br\]\]/i
|
441
|
+
@tree.tag(:br) # newline
|
442
|
+
when str =~ /\A__/
|
443
|
+
toggle_tag 'u', $& # underline
|
444
|
+
when str =~ /\A~~/
|
445
|
+
toggle_tag 'del', $& # delete
|
446
|
+
when str =~ /\A~/
|
447
|
+
@tree.add_raw(' ') # tilde
|
448
|
+
# when /\A\+\+/
|
449
|
+
# toggle_tag 'ins', $& # insert
|
450
|
+
when str =~ /\A\^/
|
451
|
+
toggle_tag 'sup', $& # ^{}
|
452
|
+
when str =~ /\A,,/
|
453
|
+
toggle_tag 'sub', $& # _{}
|
454
|
+
when str =~ /\A!(\{\{|[^\s])/
|
455
|
+
@tree.add($1) # !neco !{{
|
456
|
+
when str =~ /\A./
|
457
|
+
@tree.add($&) # ordinal char
|
461
458
|
end
|
462
|
-
|
463
|
-
|
464
|
-
end
|
465
|
-
@tree.tag_end(:a)
|
466
|
-
end
|
467
|
-
|
468
|
-
def parse_inline_tag(str)
|
469
|
-
case
|
470
|
-
when raw_html? && str =~ /\A<(\/)?(\w+)(?:([^>]*?))?(\/\s*)?>/ # single inline <html> tag
|
471
|
-
eot, tag, args, closed = $1, $2, $3, $4
|
472
|
-
do_raw_tag(eot, tag, args, closed, $'.size)
|
473
|
-
when str =~ /\A\{\{\{(.*?\}*)\}\}\}/ # inline {{{ }}} pre (tt)
|
474
|
-
@tree.tag(:tt, $1)
|
475
|
-
when str =~ MACRO_BEG_REX # macro {{
|
476
|
-
str, lines = parse_macro($1, $')
|
477
|
-
#print "MACRO.inline(#{$1}), next:#{str}"
|
478
|
-
return str
|
479
|
-
when str =~ /\A`(.*?)`/ # inline pre (tt)
|
480
|
-
@tree.tag(:tt, $1)
|
481
|
-
when math? && str =~ /\A\$(.+?)\$/ # inline math (tt)
|
482
|
-
#@tree.add("\\( #{$1} \\)")
|
483
|
-
@tree.tag(:span, {class:'math'}, $1)
|
484
|
-
@was_math = true
|
485
|
-
when str =~ /\A(\&\w*;)/ # html entity
|
486
|
-
#print "add html ent: #{$1}\n"
|
487
|
-
@tree.add_raw($1)
|
488
|
-
when str =~ /\A([:alpha:]|[:digit:])+/
|
489
|
-
@tree.add($&) # word
|
490
|
-
when str =~ /\A\s+/
|
491
|
-
@tree.add_spc
|
492
|
-
when str =~ /\A'''''/
|
493
|
-
toggle_tag 'strongem', $& # bolditallic
|
494
|
-
when str =~ /\A\*\*/ || str =~ /\A'''/
|
495
|
-
toggle_tag 'strong', $& # bold
|
496
|
-
when str =~ /\A''/ || str =~ /\A\/\//
|
497
|
-
toggle_tag 'em', $& # italic
|
498
|
-
when str =~ /\A\\\\/ || str =~ /\A\[\[br\]\]/i
|
499
|
-
@tree.tag(:br) # newline
|
500
|
-
when str =~ /\A__/
|
501
|
-
toggle_tag 'u', $& # underline
|
502
|
-
when str =~ /\A~~/
|
503
|
-
toggle_tag 'del', $& # delete
|
504
|
-
when str =~ /\A~/
|
505
|
-
@tree.add_raw(' ') # tilde
|
506
|
-
# when /\A\+\+/
|
507
|
-
# toggle_tag 'ins', $& # insert
|
508
|
-
when str =~ /\A\^/
|
509
|
-
toggle_tag 'sup', $& # ^{}
|
510
|
-
when str =~ /\A,,/
|
511
|
-
toggle_tag 'sub', $& # _{}
|
512
|
-
when str =~ /\A!(\{\{|[^\s])/
|
513
|
-
@tree.add($1) # !neco !{{
|
514
|
-
when str =~ /./
|
515
|
-
@tree.add($&) # ordinal char
|
459
|
+
str = $'
|
460
|
+
offset += $&.size
|
516
461
|
end
|
517
|
-
return
|
462
|
+
return offset
|
518
463
|
end
|
519
464
|
|
520
465
|
#################################################################
|
521
466
|
# macro {{ }}
|
522
467
|
# convetntion {{!cmd}} {{template}} {{$var}} {{# comment}} {{!}} (pipe)
|
523
468
|
|
524
|
-
# r: expanded macro
|
469
|
+
# r: expanded macro , rest of str, count lines taken from str
|
525
470
|
# sideefect: parse result of macro
|
526
|
-
def parse_macro(macro_name, str)
|
471
|
+
def parse_macro(macro_name, str, offset, macro_name_size)
|
472
|
+
raise "offset is nil" if offset.nil?
|
473
|
+
raise "offset is nil" if macro_name_size.nil?
|
527
474
|
@env = Env.new(self) if @env.nil?
|
475
|
+
@env.atput('offset', offset)
|
476
|
+
@env.atput('lineno', @line_no)
|
528
477
|
begin
|
529
|
-
mac_out, rest, lines = @env.parse_macro_all(macro_name, str)
|
478
|
+
mac_out, rest, lines, rest_offset = @env.parse_macro_all(macro_name, str, macro_name_size)
|
479
|
+
raise "lines is nil" if lines.nil?
|
530
480
|
#print "mac: '#{mac_out}' rest: '#{rest}'\n"
|
531
|
-
|
481
|
+
#print "mac: ro #{rest_offset}, of#{offset}, lines: #{lines} ms: #{macro_name_size} strlen#{str.size}, str'#{str}' rest:'#{rest}'\n"
|
482
|
+
rest_offset += offset + macro_name_size if lines == 0
|
483
|
+
#print "ro#{rest_offset}\n"
|
484
|
+
return mac_out, rest, lines, rest_offset
|
532
485
|
rescue TooLongException => e
|
533
|
-
return "TOO_LONG_EXPANSION_OF_MACRO(#{macro_name})QUIT", 0
|
486
|
+
return '', "TOO_LONG_EXPANSION_OF_MACRO(#{macro_name})QUIT", 0, 0
|
487
|
+
rescue Exception => e
|
488
|
+
#@tree.tag(:span, {:title => "#{e}\", :class=>'parse-error'}, "!!!")
|
489
|
+
@tree.tag(:span, {:title => "#{e}\n#{e.backtrace}", :class=>'parse-error'}, "!!!")
|
490
|
+
print "tace#{e.backtrace.to_s}\n"
|
491
|
+
return '', '', 0, 0
|
534
492
|
end
|
535
493
|
end
|
536
494
|
|
495
|
+
#################################################################
|
537
496
|
|
497
|
+
def make_link(link, content, whole, offset, not_make_link = false )
|
498
|
+
# was '!' before url?
|
499
|
+
return @tree.add(whole) if not_make_link
|
538
500
|
|
539
|
-
|
501
|
+
# specail "link" [[BR]]:
|
502
|
+
return @tree.tag(:br) if link =~ /^br$/i
|
540
503
|
|
504
|
+
uri = make_explicit_link(link)
|
505
|
+
return @tree.add(whole) if not uri
|
506
|
+
return @tree.add(whole) if no_link? && uri !~ /^(ftp|https?):/
|
507
|
+
|
508
|
+
@tree.tag_beg(:a, {href:uri})
|
509
|
+
if content
|
510
|
+
parse_inline(content, offset)
|
511
|
+
else
|
512
|
+
@tree.add(link)
|
513
|
+
end
|
514
|
+
@tree.tag_end(:a)
|
515
|
+
end
|
516
|
+
|
517
|
+
#################################################################
|
541
518
|
|
542
519
|
def parse_table_row(str)
|
520
|
+
offset = 0;
|
543
521
|
start_tag('tr') if !@stack.include?('tr')
|
544
522
|
str.sub!(/\r/, '')
|
545
523
|
colspan = 1
|
@@ -548,7 +526,8 @@ module TracWiki
|
|
548
526
|
last_txt = ''
|
549
527
|
str.scan(/(=?)(\s*)(.*?)\1?($ | \|\|\\\s*$ | \|\| )/x) do
|
550
528
|
tdth = $1.empty? ? 'td' : 'th'
|
551
|
-
|
529
|
+
tdth_size = $1.size
|
530
|
+
le, txt, tail, cell_size = $2.size, $3, $4, $&.size
|
552
531
|
|
553
532
|
# do not end row, continue on next line
|
554
533
|
print_tr = false if tail =~ /^\|\|\\/
|
@@ -571,19 +550,21 @@ module TracWiki
|
|
571
550
|
start_tag(tdth, { style:style, colspan: colspan});
|
572
551
|
colspan = 1
|
573
552
|
|
574
|
-
parse_inline(txt.strip) if txt
|
553
|
+
parse_inline(txt.strip, offset + tdth_size + le + 2) if txt
|
575
554
|
end_tag while @stack.last != 'tr'
|
555
|
+
offset += cell_size
|
576
556
|
end
|
577
557
|
if print_tr
|
578
558
|
end_tag
|
579
559
|
end
|
560
|
+
return offset
|
580
561
|
end
|
581
562
|
|
582
563
|
def make_nowikiblock(input)
|
583
564
|
input.gsub(/^ (?=\}\}\})/, '')
|
584
565
|
end
|
585
566
|
|
586
|
-
def parse_li_line(spc_size, bullet
|
567
|
+
def parse_li_line(spc_size, bullet)
|
587
568
|
|
588
569
|
while !@stacki.empty? && @stacki.last > spc_size
|
589
570
|
end_tag
|
@@ -623,7 +604,6 @@ module TracWiki
|
|
623
604
|
end
|
624
605
|
|
625
606
|
start_tag('li')
|
626
|
-
parse_inline(text)
|
627
607
|
|
628
608
|
end
|
629
609
|
|
@@ -698,12 +678,12 @@ module TracWiki
|
|
698
678
|
@tree.tag(:hr)
|
699
679
|
end
|
700
680
|
|
701
|
-
def do_heading(level, title, aname)
|
681
|
+
def do_heading(level, title, aname, title_offset)
|
702
682
|
aname= aname_nice(aname, title)
|
703
683
|
@headings.last[:eline] = @line_no - 1
|
704
684
|
@headings.push({ :title => title, :sline => @line_no, :aname => aname, :level => level, })
|
705
685
|
end_paragraph
|
706
|
-
make_headline(level, title, aname)
|
686
|
+
make_headline(level, title, aname, title_offset)
|
707
687
|
end
|
708
688
|
def do_table_row(text)
|
709
689
|
if !@stack.include?('table')
|
@@ -720,43 +700,48 @@ module TracWiki
|
|
720
700
|
start_tag('dd')
|
721
701
|
end
|
722
702
|
|
723
|
-
def do_citation(level
|
703
|
+
def do_citation(level)
|
724
704
|
start_paragraph if !@stack.include? 'p'
|
725
705
|
blockquote_level_to(level)
|
726
|
-
parse_inline(quote.strip)
|
727
706
|
end
|
728
707
|
|
729
|
-
def do_ord_line(spc_size
|
730
|
-
text.rstrip!
|
708
|
+
def do_ord_line(spc_size)
|
731
709
|
|
732
710
|
if @stack.include?('li') || @stack.include?('dl')
|
733
711
|
|
734
712
|
# dl, li continuation
|
735
|
-
parse_inline(' ')
|
736
|
-
parse_inline(text)
|
713
|
+
parse_inline(' ', 0)
|
737
714
|
|
738
715
|
elsif spc_size > 0
|
739
716
|
# quote continuation
|
740
717
|
start_paragraph if !@stack.include? 'p'
|
741
718
|
blockquote_level_to(1)
|
742
|
-
parse_inline(text)
|
743
719
|
|
744
720
|
else
|
745
721
|
# real ordinary line
|
746
722
|
start_paragraph
|
747
|
-
parse_inline(text)
|
748
723
|
end
|
749
724
|
end
|
750
725
|
|
751
|
-
def parse_block(str)
|
726
|
+
def parse_block(str, want_end_paragraph = true)
|
752
727
|
#print "BLOCK.str(#{str})\n"
|
753
728
|
until str.empty?
|
754
729
|
case
|
755
730
|
# macro
|
756
|
-
when str =~ MACRO_BEG_REX
|
757
|
-
str, lines = parse_macro($1, $')
|
758
|
-
|
759
|
-
|
731
|
+
when macros? && str =~ MACRO_BEG_REX
|
732
|
+
mac, str, lines, offset = parse_macro($1, $', 0, $&.size)
|
733
|
+
raise 'lines is nil' if lines.nil?
|
734
|
+
raise 'offset is nil' if offset.nil?
|
735
|
+
#print "MACRO.lines(#{$1})lines:#{lines}, str:'#{str}'\n"
|
736
|
+
add_line_no(lines)
|
737
|
+
@count_lines_level +=1
|
738
|
+
parse_block(mac, false)
|
739
|
+
@count_lines_level -=1
|
740
|
+
if mac.size > 0 && str =~ /^(.*)(\r?\n)?/
|
741
|
+
line, str = $1 , $'
|
742
|
+
add_line_no($&.count("\n"))
|
743
|
+
parse_inline(line, offset)
|
744
|
+
end
|
760
745
|
next
|
761
746
|
# display math $$
|
762
747
|
when math? && str =~ /\A\$\$(.*?)\$\$/m
|
@@ -772,8 +757,8 @@ module TracWiki
|
|
772
757
|
do_hr()
|
773
758
|
# heading == Wiki Ruless ==
|
774
759
|
# heading == Wiki Ruless == #tag
|
775
|
-
when str =~ /\A[[:blank:]]*(={1,6})\s*(.*?)\s*=*\s*(#(\S*))?\s*$(\r?\n)?/
|
776
|
-
do_heading($
|
760
|
+
when str =~ /\A([[:blank:]]*(={1,6})\s*)(.*?)\s*=*\s*(#(\S*))?\s*$(\r?\n)?/
|
761
|
+
do_heading($2.size, $3, $5, $1.size)
|
777
762
|
# table row ||
|
778
763
|
when str =~ /\A[ \t]*\|\|(.*)$(\r?\n)?/
|
779
764
|
do_table_row($1)
|
@@ -783,31 +768,32 @@ module TracWiki
|
|
783
768
|
when str =~ /\A([:\w\s]+)::(\s+|\r?\n)/
|
784
769
|
do_term($1)
|
785
770
|
# li
|
786
|
-
when str =~ /\A(\s*)([*-]|[aAIi\d]\.)\s+(.*?)$(\r?\n)?/
|
787
|
-
parse_li_line($
|
771
|
+
when str =~ /\A((\s*)([*-]|[aAIi\d]\.)\s+)(.*?)$(\r?\n)?/
|
772
|
+
parse_li_line($2.size, $3)
|
773
|
+
parse_inline($4, $1.size)
|
788
774
|
# citation
|
789
775
|
when str =~ /\A(>[>\s]*)(.*?)$(\r?\n)?/
|
790
|
-
do_citation($1.count('>')
|
776
|
+
do_citation($1.count('>'))
|
777
|
+
parse_inline($2, $1.size)
|
791
778
|
# ordinary line
|
792
779
|
when str =~ /\A(\s*)(\S+.*?)$(\r?\n)?/
|
793
|
-
|
780
|
+
text = $2
|
781
|
+
do_ord_line($1.size)
|
782
|
+
parse_inline(text.rstrip, $1.size)
|
794
783
|
else # case str
|
795
784
|
raise "Parse error at #{str[0,30].inspect}"
|
796
785
|
end
|
797
|
-
|
786
|
+
add_line_no(($`).count("\n")+($&).count("\n"))
|
798
787
|
str = $'
|
799
788
|
end
|
800
|
-
end_paragraph
|
789
|
+
end_paragraph if want_end_paragraph
|
801
790
|
@headings.last[:eline] = @line_no - 1
|
802
791
|
end
|
803
|
-
|
804
792
|
def aname_nice(aname, title)
|
805
793
|
|
806
794
|
if aname.nil? && id_from_heading?
|
807
795
|
aname = title.gsub /\s+/, '_'
|
808
|
-
if id_translit?
|
809
|
-
aname = Iconv.iconv('ascii//translit', 'utf-8', aname).join
|
810
|
-
end
|
796
|
+
aname = _translit(aname) if id_translit?
|
811
797
|
end
|
812
798
|
return nil if aname.nil?
|
813
799
|
aname_ori = aname
|
@@ -819,5 +805,13 @@ module TracWiki
|
|
819
805
|
@anames[aname] = true
|
820
806
|
aname
|
821
807
|
end
|
808
|
+
def _translit(text)
|
809
|
+
# iconv is obsolete, but translit funcionality was not replaced
|
810
|
+
# see http://stackoverflow.com/questions/20224915/iconv-will-be-deprecated-in-the-future-transliterate
|
811
|
+
# return Iconv.iconv('ascii//translit', 'utf-8', text).join
|
812
|
+
|
813
|
+
# http://unicode-utils.rubyforge.org/UnicodeUtils.html#method-c-compatibility_decomposition
|
814
|
+
return UnicodeUtils.compatibility_decomposition(text).chars.grep(/\p{^Mn}/).join('')
|
815
|
+
end
|
822
816
|
end
|
823
817
|
end
|