trac-wiki 0.2.24 → 0.3.10

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.
@@ -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