erbook 6.1.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (121) hide show
  1. data/bin/erbook +29 -25
  2. data/doc/HelloWorld.spec +3 -1
  3. data/doc/api/classes/ERBook.html +86 -0
  4. data/doc/api/classes/ERBook/Document.html +399 -0
  5. data/doc/api/classes/ERBook/Document/Node.html +110 -0
  6. data/doc/api/classes/ERBook/Template.html +309 -0
  7. data/doc/api/classes/ERBook/Template/Sandbox.html +134 -0
  8. data/doc/api/classes/RDoc.html +69 -0
  9. data/doc/api/classes/RDoc/AnyMethod.html +249 -0
  10. data/doc/api/classes/RDoc/DummyMarkup.html +60 -0
  11. data/doc/api/classes/RDoc/DummyMixin.html +54 -0
  12. data/doc/api/classes/RDoc/DummyOptions.html +60 -0
  13. data/doc/api/classes/RDoc/TopLevel.html +344 -0
  14. data/doc/api/classes/String.html +212 -0
  15. data/doc/api/created.rid +1 -0
  16. data/doc/api/css/main.css +263 -0
  17. data/doc/api/css/panel.css +383 -0
  18. data/doc/api/css/reset.css +53 -0
  19. data/doc/api/files/ANN_eml.html +276 -0
  20. data/doc/api/files/ANN_html.html +407 -0
  21. data/doc/api/files/ANN_txt.html +271 -0
  22. data/doc/api/files/LICENSE.html +76 -0
  23. data/doc/api/files/lib/erbook/document_rb.html +74 -0
  24. data/doc/api/files/lib/erbook/rdoc_rb.html +77 -0
  25. data/doc/api/files/lib/erbook/template_rb.html +66 -0
  26. data/doc/api/files/lib/erbook/to_xhtml_rb.html +89 -0
  27. data/doc/api/files/lib/erbook_rb.html +70 -0
  28. data/doc/api/i/arrows.png +0 -0
  29. data/doc/api/i/results_bg.png +0 -0
  30. data/doc/api/i/tree_bg.png +0 -0
  31. data/doc/api/index.html +14 -18
  32. data/doc/api/js/jquery-1.3.2.min.js +19 -0
  33. data/doc/api/js/jquery-effect.js +593 -0
  34. data/doc/api/js/main.js +22 -0
  35. data/doc/api/js/searchdoc.js +605 -0
  36. data/doc/api/panel/index.html +63 -0
  37. data/doc/api/panel/search_index.js +1 -0
  38. data/doc/api/panel/tree.js +1 -0
  39. data/doc/formats.erb +63 -115
  40. data/doc/history.erb +125 -92
  41. data/doc/index.erb +12 -7
  42. data/doc/index.xhtml +1020 -1347
  43. data/doc/intro.erb +42 -40
  44. data/doc/setup.erb +18 -18
  45. data/doc/theory.erb +24 -31
  46. data/doc/usage.erb +14 -24
  47. data/fmt/xhtml.icons/index.yaml +16 -14
  48. data/fmt/xhtml.icons/rakefile +33 -0
  49. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/16x16/places/start-here.png +0 -0
  50. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/32x32/actions/go-down.png +0 -0
  51. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/32x32/actions/go-up.png +0 -0
  52. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/32x32/places/start-here.png +0 -0
  53. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/32x32/status/software-update-available.png +0 -0
  54. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/48x48/apps/accessories-text-editor.png +0 -0
  55. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/48x48/apps/internet-group-chat.png +0 -0
  56. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/48x48/emblems/emblem-important.png +0 -0
  57. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/48x48/status/dialog-error.png +0 -0
  58. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/48x48/status/dialog-information.png +0 -0
  59. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/48x48/status/dialog-warning.png +0 -0
  60. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/COPYING +1 -0
  61. data/fmt/xhtml.icons/{tango-icon-theme-0.8.1 → tango-icon-theme-0.8.90}/README +0 -0
  62. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/actions/bookmark-new.svg +672 -0
  63. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/actions/go-down.svg +200 -0
  64. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/actions/go-home.svg +445 -0
  65. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/actions/go-up.svg +196 -0
  66. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/actions/view-refresh.svg +393 -0
  67. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/apps/accessories-text-editor.svg +554 -0
  68. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/apps/internet-group-chat.svg +312 -0
  69. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/emblems/emblem-favorite.svg +242 -0
  70. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/emblems/emblem-important.svg +164 -0
  71. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/mimetypes/application-certificate.svg +443 -0
  72. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/places/start-here.svg +492 -0
  73. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/status/dialog-error.svg +330 -0
  74. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/status/dialog-information.svg +1159 -0
  75. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/status/dialog-warning.svg +373 -0
  76. data/fmt/xhtml.icons/tango-icon-theme-0.8.90/scalable/status/software-update-available.svg +209 -0
  77. data/fmt/xhtml.scripts/jquery-1.3.2.min.js +19 -0
  78. data/fmt/xhtml.scripts/jquery.localscroll-1.2.7-min.js +9 -0
  79. data/fmt/xhtml.scripts/jquery.scrollTo-1.4.1-min.js +11 -0
  80. data/fmt/xhtml.yaml +470 -454
  81. data/lang/de.yaml +5 -5
  82. data/lang/el.yaml +4 -4
  83. data/lang/es.yaml +5 -5
  84. data/lang/fr.yaml +5 -5
  85. data/lang/it.yaml +5 -5
  86. data/lang/ja.yaml +4 -4
  87. data/lang/ko.yaml +4 -4
  88. data/lang/nl.yaml +4 -4
  89. data/lang/phrases.yaml +4 -4
  90. data/lang/pt.yaml +5 -5
  91. data/lang/ru.yaml +5 -5
  92. data/lang/zh.yaml +5 -5
  93. data/lang/zt.yaml +5 -5
  94. data/lib/erbook.rb +12 -5
  95. data/lib/erbook/document.rb +127 -107
  96. data/lib/erbook/rdoc.rb +18 -11
  97. data/lib/erbook/template.rb +86 -176
  98. data/lib/erbook/to_xhtml.rb +98 -25
  99. data/{Rakefile → rakefile} +1 -0
  100. metadata +106 -53
  101. data/doc/api/ERBook.html +0 -35
  102. data/doc/api/ERBook/Document.html +0 -669
  103. data/doc/api/ERBook/Document/Node.html +0 -102
  104. data/doc/api/ERBook/Template.html +0 -670
  105. data/doc/api/RDoc.html +0 -23
  106. data/doc/api/RDoc/AnyMethod.html +0 -302
  107. data/doc/api/RDoc/DummyMarkup.html +0 -73
  108. data/doc/api/RDoc/DummyMixin.html +0 -23
  109. data/doc/api/RDoc/DummyOptions.html +0 -140
  110. data/doc/api/RDoc/TopLevel.html +0 -465
  111. data/doc/api/String.html +0 -372
  112. data/doc/api/all-methods.html +0 -253
  113. data/doc/api/all-namespaces.html +0 -42
  114. data/doc/api/app.js +0 -18
  115. data/doc/api/jquery.js +0 -11
  116. data/doc/api/readme.html +0 -38
  117. data/doc/api/style.css +0 -68
  118. data/doc/api/syntax_highlight.css +0 -21
  119. data/fmt/xhtml.icons/tango-icon-theme-0.8.1/48x48/README +0 -2
  120. data/fmt/xhtml.icons/tango-icon-theme-0.8.1/48x48/apps/internet-group-chat.png +0 -0
  121. data/fmt/xhtml.icons/tango-icon-theme-0.8.1/COPYING +0 -67
data/lib/erbook/rdoc.rb CHANGED
@@ -1,17 +1,23 @@
1
- # Workaround for: `rdoc --fmt xml` does not dump information about methods.
2
-
1
+ # Workaround for `rdoc --fmt xml` not dumping information about methods.
2
+ #--
3
+ # Copyright 2008 Suraj N. Kurapati
4
+ # See the LICENSE file for details.
5
+ #++
6
+
7
+ require 'rubygems'
8
+ gem 'rdoc', '>= 2.4.3', '< 2.5'
3
9
  require 'rdoc/rdoc'
4
10
 
5
11
  module RDoc
6
12
  class TopLevel
7
13
  # Returns an array of all classes recorded thus far.
8
14
  def self.all_classes
9
- @@all_classes.values
15
+ @all_classes.values
10
16
  end
11
17
 
12
18
  # Returns an array of all modules recorded thus far.
13
19
  def self.all_modules
14
- @@all_modules.values
20
+ @all_modules.values
15
21
  end
16
22
 
17
23
  # Returns an array of RDoc::AnyMethod objects
@@ -25,10 +31,10 @@ module RDoc
25
31
  def self.refresh_all_classes_and_modules
26
32
  visit = lambda do |node|
27
33
  if node.is_a? NormalClass or node.is_a? SingleClass
28
- @@all_classes[node.full_name] = node
34
+ @all_classes[node.full_name] = node
29
35
 
30
36
  elsif node.is_a? NormalModule
31
- @@all_modules[node.full_name] = node
37
+ @all_modules[node.full_name] = node
32
38
  end
33
39
 
34
40
  (node.classes + node.modules).each {|n| visit[n] }
@@ -48,14 +54,15 @@ module RDoc
48
54
  # RDoc will ignore the given code string! :-(
49
55
  #
50
56
  def self.parse aCodeString, aFileName = __FILE__
51
- top = ParserFactory.parser_for(
52
- TopLevel.new(aFileName), aFileName,
53
- aCodeString, DummyOptions.new, Stats.new
54
- ).scan
57
+ tl = TopLevel.new(aFileName)
58
+ op = DummyOptions.new
59
+ st = Stats.new(0)
60
+
61
+ result = Parser.for(tl, aFileName, aCodeString, op, st).scan
55
62
 
56
63
  refresh_all_classes_and_modules
57
64
 
58
- top
65
+ result
59
66
  end
60
67
 
61
68
  # Returns a RDoc::TopLevel object containing information
@@ -1,210 +1,120 @@
1
- require 'erb'
2
- require 'pathname'
1
+ #--
2
+ # Copyright 2008 Suraj N. Kurapati
3
+ # See the LICENSE file for details.
4
+ #++
5
+
6
+ require 'ember'
3
7
 
4
8
  module ERBook
5
9
  ##
6
- # An eRuby template which allows access to the underlying result
10
+ # eRuby template that provides access to the underlying result
7
11
  # buffer (which contains the result of template evaluation thus
8
12
  # far) and provides sandboxing for isolated template rendering.
9
13
  #
10
- # In addition to the standard <% eRuby %> directives, this template supports:
11
- #
12
- # * Lines that begin with '%' are treated as normal eRuby directives.
13
- #
14
- # * Include directives (<%#include YOUR_PATH #%>) are replaced by the result
15
- # of reading and evaluating the YOUR_PATH file in the current context.
16
- #
17
- # * Unless YOUR_PATH is an absolute path, it is treated as being
18
- # relative to the file which contains the include directive.
19
- #
20
- # * Errors originating from included files are given a proper
21
- # stack trace which shows the chain of inclusion plus any
22
- # further trace steps originating from the included file itself.
23
- #
24
- # * eRuby directives delimiting Ruby blocks (<% ... do %>
25
- # ... <% end %>) can be heirarchically unindented by the
26
- # crown margin of the opening (<% ... do %>) delimiter.
27
- #
28
- class Template < ERB
29
- # The result of template evaluation thus far.
30
- attr_reader :buffer
14
+ class Template < Ember::Template
15
+ attr_reader :sandbox
31
16
 
32
17
  ##
33
- # @param [String] source
18
+ # ==== Parameters
19
+ #
20
+ # [source]
34
21
  # Replacement for the ambiguous '(erb)' identifier in stack traces;
35
22
  # so that the user can better determine the source of an error.
36
23
  #
37
- # @param [String] input
24
+ # [input]
38
25
  # A string containing eRuby directives.
39
26
  #
40
- # @param [boolean] unindent
27
+ # [unindent]
41
28
  # If true, then all content blocks will be unindented hierarchically,
42
- # by the leading space of their 'do' and 'end' delimiters.
43
29
  #
44
- # @param safe_level
30
+ # [safe_level]
45
31
  # See safe_level in ERB::new().
46
32
  #
47
- def initialize source, input, unindent = false, safe_level = nil
48
- # expand all "include" directives in the input
49
- expander = lambda do |src_file, src_text, path_stack, stack_trace|
50
- src_path = File.expand_path(src_file)
51
- src_line = 1 # line number of the current include directive in src_file
52
-
53
- chunks = src_text.split(/<%#\s*include\s+(.+?)\s*#%>/)
54
-
55
- path_stack.push src_path
56
- chunks.each_with_index do |chunk, i|
57
- # even number: chunk is not an include directive
58
- if i & 1 == 0
59
- src_line += chunk.count("\n")
60
-
61
- # odd number: chunk is the target of the include directive
62
- else
63
- # resolve correct path of target file
64
- dst_file = chunk
65
-
66
- unless Pathname.new(dst_file).absolute?
67
- # target is relative to the file in
68
- # which the include directive exists
69
- dst_file = File.join(File.dirname(src_file), dst_file)
70
- end
71
-
72
- dst_path = File.expand_path(dst_file)
73
-
74
- # include the target file
75
- if path_stack.include? dst_file
76
- raise "Cannot include #{dst_file.inspect} at #{src_file.inspect}:#{src_line} because that would cause an infinite loop in the inclusion stack: #{path_stack.inspect}."
77
- else
78
- stack_trace.push "#{src_path}:#{src_line}"
79
- dst_text = eval('File.read dst_file', binding, src_file, src_line)
80
-
81
- # recursively expand any include directives within
82
- # the expansion of the current include directive
83
- dst_text = expander[dst_file, dst_text, path_stack, stack_trace]
84
-
85
- # provide more accurate stack trace for
86
- # errors originating from included files
87
- line_var = "__erbook_var_#{dst_file.object_id.abs}__"
88
- dst_text = %{<%
89
- #{line_var} = __LINE__ + 2 # content is 2 newlines below
90
- begin
91
- %>#{dst_text}<%
92
- rescue Exception => err
93
- bak = err.backtrace
94
-
95
- top = []
96
- found_top = false
97
- prev_line = nil
98
-
99
- bak.each do |step|
100
- if step =~ /^#{/#{source}/}:(\\d+)(.*)/
101
- line, desc = $1, $2
102
- line = line.to_i - #{line_var} + 1
103
-
104
- if line > 0 and line != prev_line
105
- top << "#{dst_path}:\#{line}\#{desc}"
106
- found_top = true
107
- prev_line = line
108
- end
109
- elsif !found_top
110
- top << step
111
- end
112
- end
113
-
114
- if found_top
115
- bak.replace top
116
- bak.concat #{stack_trace.reverse.inspect}
117
- end
118
-
119
- raise err
120
- end
121
- %>}
122
-
123
- stack_trace.pop
124
- end
125
-
126
- chunks[i] = dst_text
127
- end
128
- end
129
- path_stack.pop
130
-
131
- chunks.join
132
- end
133
-
134
- input = expander[source, input, [], []]
135
-
136
- # convert "% at beginning of line" usage into <% normal %> usage
137
- input.gsub! %r{^([ \t]*)(%[=# \t].*)$}, '\1<\2 %>'
138
- input.gsub! %r{^([ \t]*)%%}, '\1%'
139
-
140
- # unindent node content hierarchically
141
- if unindent
142
- tags = input.scan(/<%(?:.(?!<%))*?%>/m)
143
- margins = []
144
- result = []
145
-
146
- buffer = input
147
- tags.each do |tag|
148
- chunk, buffer = buffer.split(tag, 2)
149
- chunk << tag
150
-
151
- # perform unindentation
152
- result << chunk.gsub(/^#{margins.last}/, '')
153
-
154
- # prepare for next unindentation
155
- case tag
156
- when /<%[^%=].*?\bdo\b.*?%>/m
157
- margins.push buffer[/^[ \t]*(?=\S)/]
158
-
159
- when /<%\s*end\s*%>/m
160
- margins.pop
161
- end
162
- end
163
- result << buffer
164
-
165
- input = result.join
33
+ def initialize source, input, unindent = true, safe_level = nil
34
+ super input,
35
+ :result_variable => :@buffer,
36
+ :source_file => source,
37
+ :unindent => unindent,
38
+ :shorthand => true,
39
+ :infer_end => true
40
+
41
+ @sandbox = Sandbox.new
42
+
43
+ if $DEBUG
44
+ IO.popen('cat -n', 'w+') do |io|
45
+ io.write self.program
46
+ io.close_write
47
+ STDERR.puts io.read
166
48
  end
49
+ end
50
+ end
167
51
 
168
- # silence the code-only <% ... %> directive, just like PHP does
169
- input.gsub! %r{^[ \t]*(<%[^%=]((?!<%).)*?[^%]%>)[ \t]*\r?\n}m, '\1'
170
-
171
- # use @buffer to store the result of the ERB template
172
- super input, safe_level, nil, :@buffer
52
+ ##
53
+ # Returns the output of evaluating this template inside the given context.
54
+ #
55
+ # If no context is given, then the sandbox of this template is used.
56
+ #
57
+ def render
58
+ super @sandbox.instance_eval('binding')
59
+ end
173
60
 
174
- self.filename = source
61
+ ##
62
+ # Returns the result of template evaluation thus far.
63
+ #
64
+ def buffer
65
+ @sandbox.instance_variable_get(:@buffer)
175
66
  end
176
67
 
177
- # Renders this template within a fresh object that is populated with
68
+ ##
69
+ # Renders this template within a fresh sandbox that is populated with
178
70
  # the given instance variables, whose names must be prefixed with '@'.
71
+ #
179
72
  def render_with inst_vars = {}
180
- context = Object.new.instance_eval do
73
+ old_sandbox = @sandbox
74
+
75
+ begin
76
+ @sandbox = Sandbox.new
77
+
181
78
  inst_vars.each_pair do |var, val|
182
- instance_variable_set var, val
79
+ @sandbox.instance_variable_set var, val
183
80
  end
184
81
 
185
- binding
186
- end
82
+ render
187
83
 
188
- result context
84
+ ensure
85
+ @sandbox = old_sandbox
86
+ end
189
87
  end
190
88
 
191
- protected
192
-
193
- # Returns the content that the given block wants to append to
194
- # the buffer. If the given block does not want to append to the
195
- # buffer, then returns the result of invoking the given block.
196
- def content_from_block *block_args
197
- raise ArgumentError, 'block must be given' unless block_given?
198
-
199
- head = @buffer.length
200
- body = yield(*block_args) # this appends 'content' to '@buffer'
201
- tail = @buffer.length
89
+ ##
90
+ # Environment for template evaluation.
91
+ #
92
+ class Sandbox
93
+ ##
94
+ # Returns an array of things that the given
95
+ # block wants to append to the buffer. If
96
+ # the given block does not want to append
97
+ # to the buffer, then returns the result of
98
+ # invoking the given block inside an array.
99
+ #
100
+ def __block_content__ *block_args
101
+ raise ArgumentError, 'block must be given' unless block_given?
102
+
103
+ original = @buffer
104
+
105
+ begin
106
+ block_content = @buffer = []
107
+ return_value = yield(*block_args) # this appends content to @buffer
108
+ ensure
109
+ @buffer = original
110
+ end
202
111
 
203
- if tail > head
204
- @buffer.slice! head..tail
205
- else
206
- body
207
- end.to_s
112
+ if block_content.empty?
113
+ [return_value]
114
+ else
115
+ block_content
116
+ end
117
+ end
208
118
  end
209
119
  end
210
120
  end
@@ -4,13 +4,18 @@
4
4
  # This particular implementation features the Markdown
5
5
  # formatting system via Maruku, syntax coloring via CodeRay,
6
6
  # and smart source code sizing (block versus inline display).
7
+ #--
8
+ # Copyright 2006 Suraj N. Kurapati
9
+ # See the LICENSE file for details.
10
+ #++
7
11
 
8
12
  require 'cgi'
13
+ require 'digest/sha1'
9
14
 
10
15
  begin
11
16
  require 'rubygems'
12
17
  gem 'maruku', '~> 0.5'
13
- gem 'coderay', '>= 0.7'
18
+ gem 'coderay', '>= 0.8'
14
19
  rescue LoadError
15
20
  end
16
21
 
@@ -22,23 +27,33 @@ class String
22
27
  # they are being processed by Textile. By doing this, we
23
28
  # avoid unwanted Textile transformations, such as quotation
24
29
  # marks becoming curly (&#8192;), in source code.
25
- PROTECTED_TAGS = %w[tt code pre]
30
+ PROTECTED_TAGS = {
31
+ :pre => :block, # tag => is it a block or inline element?
32
+ :code => :inline,
33
+ :tt => :inline
34
+ }
26
35
 
27
36
  # The content of these XHTML tags will be preserved
28
37
  # *verbatim* throughout the text-to-XHTML conversion process.
29
- VERBATIM_TAGS = %w[noformat]
38
+ VERBATIM_TAGS = {
39
+ :noformat => :block # tag => is it a block or inline element?
40
+ }
30
41
 
42
+ ##
31
43
  # Transforms this string into an *inline* XHTML string (one that
32
44
  # does not contain any block-level XHTML elements at the root).
45
+ #
33
46
  def to_inline_xhtml
34
47
  to_xhtml true
35
48
  end
36
49
 
50
+ ##
37
51
  # Transforms this string into XHTML while ensuring that the
38
52
  # result contains one or more block-level elements at the root.
39
53
  #
40
- # inline:: If true, the resulting XHTML will *not*
41
- # contain a block-level element at the root.
54
+ # [inline]
55
+ # If true, the resulting XHTML will *not*
56
+ # contain a block-level element at the root.
42
57
  #
43
58
  def to_xhtml inline = false
44
59
  with_protected_tags(self, VERBATIM_TAGS, true) do |text|
@@ -63,51 +78,85 @@ class String
63
78
  end
64
79
  end
65
80
 
81
+ ##
66
82
  # Returns the result of running this string through Maruku.
67
83
  #
68
- # inline:: If true, the resulting XHTML will *not*
69
- # be wrapped in a XHTML paragraph element.
84
+ # [inline]
85
+ # If true, the resulting XHTML will *not*
86
+ # be wrapped in a XHTML paragraph element.
70
87
  #
71
88
  def thru_maruku inline = false #:nodoc:
72
- html = Maruku.new(self).to_html
89
+ #
90
+ # XXX: add a newline at the beginning of the text to
91
+ # prevent Maruku from interpreting the first line
92
+ # of text as a parameter definition, which is the
93
+ # case if that first line matches /\S{2}: /
94
+ #
95
+ # see this bug report for details:
96
+ # http://rubyforge.org/tracker/?func=detail&atid=10735&aid=25697&group_id=2795
97
+ #
98
+ html = Maruku.new("\n#{self}").to_html
73
99
  html.sub! %r{\A<p>(.*)</p>\Z}, '\1' if inline
74
100
  html
75
101
  end
76
102
 
77
- # Adds syntax coloring to <code> elements in the given text. If the
78
- # <code> tag has an attribute lang="...", then that is considered the
79
- # programming language for which appropriate syntax coloring should be
80
- # applied. Otherwise, the programming language is assumed to be ruby.
103
+ ##
104
+ # Adds syntax coloring to <code> elements in this string.
105
+ #
106
+ # Each <code> element is annotated with a class="line"
107
+ # or a class="para" attribute, according to whether it
108
+ # spans a single line or multiple lines of code.
109
+ #
110
+ # In the latter case, the <code> element is replaced with a <pre> element
111
+ # so that its multi-line body appears correctly in text-mode web browsers.
112
+ #
113
+ # If a <code> element has a lang="..." attribute,
114
+ # then that attribute's value is considered to be
115
+ # the programming language for which appropriate
116
+ # syntax coloring should be applied. Otherwise,
117
+ # the programming language is assumed to be ruby.
118
+ #
81
119
  def thru_coderay #:nodoc:
82
120
  gsub %r{<(code)(.*?)>(.*?)</\1>}m do
83
- atts, code = $2, CGI.unescapeHTML($3).sub(/\A\r?\n/, '')
121
+ elem, atts, code = $1, $2, CGI.unescapeHTML($3).sub(/\A\r?\n/, '')
84
122
  lang = atts[/\blang=('|")(.*?)\1/i, 2] || :ruby
85
123
 
86
- html = CodeRay.scan(code, lang).html(:css => :style)
87
- tag = if code =~ /\n/ then :pre else :code end
124
+ body = CodeRay.scan(code, lang).html(:css => :style)
125
+
126
+ if code =~ /\n/
127
+ span = :para
128
+ head = "<ins><pre"
129
+ tail = "</pre></ins>"
130
+
131
+ else
132
+ span = :line
133
+ head = "<#{elem}"
134
+ tail = "</#{elem}>"
135
+ end
88
136
 
89
- %{<#{tag} class="code"#{atts}>#{html}</#{tag}>}
137
+ %{#{head} class="#{span}"#{atts}>#{body}#{tail}}
90
138
  end
91
139
  end
92
140
 
93
141
  private
94
142
 
143
+ ##
95
144
  # Protects the given tags in the given input, passes
96
145
  # that protected input to the given block, restores the
97
146
  # given tags in the result of the block and returns it.
98
147
  #
99
- # verbatim:: If true, the content of the elments having the
100
- # given tags will not be temporarily altered so
101
- # that process nested elements can be processed.
148
+ # [verbatim]
149
+ # If true, the content of the elments having the given tags will not be
150
+ # temporarily altered so that process nested elements can be processed.
102
151
  #
103
- def with_protected_tags input, tags, verbatim #:nodoc: :yields: input
152
+ def with_protected_tags input, tag_defs, verbatim #:yields: input
104
153
  raise ArgumentError unless block_given?
105
154
 
106
155
  input = input.dup
107
156
  escapes = {}
108
157
 
109
158
  # protect the given tags by escaping them
110
- tags.each do |tag|
159
+ tag_defs.each_key do |tag|
111
160
  input.gsub! %r{(<#{tag}.*?>)(.*?)(</#{tag}>)}m do
112
161
  head, body, tail = $1, $2, $3
113
162
 
@@ -123,7 +172,7 @@ class String
123
172
  head << CGI.escapeHTML(CGI.unescapeHTML(body)) << tail
124
173
  end
125
174
 
126
- escaped = ERBook::Document.digest(original)
175
+ escaped = calc_digest(original)
127
176
  escapes[escaped] = original
128
177
 
129
178
  escaped
@@ -136,12 +185,36 @@ class String
136
185
  # restore the protected tags by unescaping them
137
186
  until escapes.empty?
138
187
  escapes.each_pair do |esc, orig|
139
- if output.gsub! esc, orig
140
- escapes.delete esc
141
- end
188
+ tag = orig[/<\/(.+?)>\s*\z/, 1].to_sym
189
+ raise ArgumentError, tag unless tag_defs.key? tag
190
+
191
+ restore_ok =
192
+ case tag_defs[tag]
193
+ when :inline
194
+ # process inline elements normally
195
+ output.gsub! esc, orig
196
+
197
+ when :block
198
+ # pull block-level elements out of paragraph tag added by Maruku
199
+ output.gsub!(/(<p>\s*)?#{Regexp.quote esc}/){ orig + $1.to_s }
200
+ end
201
+
202
+ escapes.delete esc if restore_ok
142
203
  end
143
204
  end
144
205
 
145
206
  output
146
207
  end
208
+
209
+ ##
210
+ # Returns a digest of the given string that
211
+ # will not be altered by String#to_xhtml.
212
+ #
213
+ def calc_digest input
214
+ Digest::SHA1.hexdigest(input.to_s).
215
+
216
+ # XXX: surround all digits with alphabets so
217
+ # Maruku doesn't change them into HTML
218
+ gsub(/\d/, 'z\&z')
219
+ end
147
220
  end