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.
@@ -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. Creole is a lightweight markup syntax similar to what many
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
- # The simplest interface is TracWiki.render. The default handling of
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 :raw_html
71
- def raw_html?; @raw_html; end
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 will had id, generated from heading text
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
- attr_accessor :plugins
92
- @plugins = {}
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(text, options = {})
103
- init_plugins
127
+ def initialize(options = nil)
128
+ init_macros
129
+ @macros = true
104
130
  @allowed_schemes = %w(http https ftp ftps)
105
- @anames = {}
106
- plugins = options.delete :plugins
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 init_plugins
116
- @plugins = {
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
- env.pp_env
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
- #"<img src=\"#{make_explicit_link(uri)}\"#{make_image_attrs(attrs)}/>"
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, text, aname)
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(text)
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 str
404
- # raw url
405
- when /\A(!)?((https?|ftps?):\/\/\S+?)(?=([\]\,.?!:;"'\)]+)?(\s|$))/
406
- str = $'
407
- if $1
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
- # [ link1 | text2 ]
422
- when /\A \[ \s* ([^\[|]*?) \s* (\|\s*(.*?))? \s* \] /mx
423
- str = $'
424
- link, content, whole= $1, $3, $&
425
- make_link(link, content, "[#{whole}]")
426
- # [[ link1 | text2 ]]
427
- when /\A \[\[ \s* ([^|]*?) \s* (\|\s*(.*?))? \s* \]\] /mx
428
- str = $'
429
- link, content, whole= $1, $3, $&
430
- make_link(link, content, whole)
431
- else
432
- str = parse_inline_tag(str)
433
- end
434
-
435
- end
436
- end
437
-
438
- def make_link(link, content, whole)
439
- # specail "link" [[BR]]:
440
- if link =~ /^br$/i
441
- @tree.tag(:br)
442
- return
443
- end
444
- uri = make_explicit_link(link)
445
- if not uri
446
- @tree.add(whole)
447
- return
448
- end
449
-
450
- if no_link?
451
- if uri !~ /^(ftp|https?):/
452
- @tree.add(whole)
453
- return
454
- end
455
- end
456
-
457
- @tree.tag_beg(:a, {href:uri})
458
- if content
459
- until content.empty?
460
- content = parse_inline_tag(content)
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('&nbsp;') # 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
- else
463
- @tree.add(link)
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('&nbsp;') # 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 + rest of str, count lines taken from str
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
- return mac_out + rest, lines
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
- le, txt, tail = $2.size, $3, $4
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, text)
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, quote)
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, text)
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
- #print "MACRO.block(#{$1})next:#{str}\n"
759
- @line_no += lines
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($1.size, $2, $4)
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($1.size, $2, $3)
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('>'), $2)
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
- do_ord_line($1.size, $2)
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
- @line_no += ($`+$&).count("\n")
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