comparison vendor/gems/coderay-0.9.7/lib/coderay/encoders/.svn/text-base/html.rb.svn-base @ 210:0579821a129a

Update to Redmine trunk rev 4802
author Chris Cannam
date Tue, 08 Feb 2011 13:51:46 +0000
parents
children
comparison
equal deleted inserted replaced
128:07fa8a8b56a8 210:0579821a129a
1 require 'set'
2
3 module CodeRay
4 module Encoders
5
6 # = HTML Encoder
7 #
8 # This is CodeRay's most important highlighter:
9 # It provides save, fast XHTML generation and CSS support.
10 #
11 # == Usage
12 #
13 # require 'coderay'
14 # puts CodeRay.scan('Some /code/', :ruby).html #-> a HTML page
15 # puts CodeRay.scan('Some /code/', :ruby).html(:wrap => :span)
16 # #-> <span class="CodeRay"><span class="co">Some</span> /code/</span>
17 # puts CodeRay.scan('Some /code/', :ruby).span #-> the same
18 #
19 # puts CodeRay.scan('Some code', :ruby).html(
20 # :wrap => nil,
21 # :line_numbers => :inline,
22 # :css => :style
23 # )
24 # #-> <span class="no">1</span> <span style="color:#036; font-weight:bold;">Some</span> code
25 #
26 # == Options
27 #
28 # === :tab_width
29 # Convert \t characters to +n+ spaces (a number.)
30 # Default: 8
31 #
32 # === :css
33 # How to include the styles; can be :class or :style.
34 #
35 # Default: :class
36 #
37 # === :wrap
38 # Wrap in :page, :div, :span or nil.
39 #
40 # You can also use Encoders::Div and Encoders::Span.
41 #
42 # Default: nil
43 #
44 # === :title
45 #
46 # The title of the HTML page (works only when :wrap is set to :page.)
47 #
48 # Default: 'CodeRay output'
49 #
50 # === :line_numbers
51 # Include line numbers in :table, :inline, :list or nil (no line numbers)
52 #
53 # Default: nil
54 #
55 # === :line_number_start
56 # Where to start with line number counting.
57 #
58 # Default: 1
59 #
60 # === :bold_every
61 # Make every +n+-th number appear bold.
62 #
63 # Default: 10
64 #
65 # === :highlight_lines
66 #
67 # Highlights certain line numbers.
68 # Can be any Enumerable, typically just an Array or Range, of numbers.
69 #
70 # Bolding is deactivated when :highlight_lines is set. It only makes sense
71 # in combination with :line_numbers.
72 #
73 # Default: nil
74 #
75 # === :hint
76 # Include some information into the output using the title attribute.
77 # Can be :info (show token type on mouse-over), :info_long (with full path)
78 # or :debug (via inspect).
79 #
80 # Default: false
81 class HTML < Encoder
82
83 include Streamable
84 register_for :html
85
86 FILE_EXTENSION = 'html'
87
88 DEFAULT_OPTIONS = {
89 :tab_width => 8,
90
91 :css => :class,
92
93 :style => :cycnus,
94 :wrap => nil,
95 :title => 'CodeRay output',
96
97 :line_numbers => nil,
98 :line_number_start => 1,
99 :bold_every => 10,
100 :highlight_lines => nil,
101
102 :hint => false,
103 }
104
105 helper :output, :css
106
107 attr_reader :css
108
109 protected
110
111 HTML_ESCAPE = { #:nodoc:
112 '&' => '&amp;',
113 '"' => '&quot;',
114 '>' => '&gt;',
115 '<' => '&lt;',
116 }
117
118 # This was to prevent illegal HTML.
119 # Strange chars should still be avoided in codes.
120 evil_chars = Array(0x00...0x20) - [?\n, ?\t, ?\s]
121 evil_chars.each { |i| HTML_ESCAPE[i.chr] = ' ' }
122 #ansi_chars = Array(0x7f..0xff)
123 #ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
124 # \x9 (\t) and \xA (\n) not included
125 #HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
126 HTML_ESCAPE_PATTERN = /[\t"&><\0-\x8\xB-\x1f]/
127
128 TOKEN_KIND_TO_INFO = Hash.new { |h, kind|
129 h[kind] =
130 case kind
131 when :pre_constant
132 'Predefined constant'
133 else
134 kind.to_s.gsub(/_/, ' ').gsub(/\b\w/) { $&.capitalize }
135 end
136 }
137
138 TRANSPARENT_TOKEN_KINDS = [
139 :delimiter, :modifier, :content, :escape, :inline_delimiter,
140 ].to_set
141
142 # Generate a hint about the given +classes+ in a +hint+ style.
143 #
144 # +hint+ may be :info, :info_long or :debug.
145 def self.token_path_to_hint hint, classes
146 title =
147 case hint
148 when :info
149 TOKEN_KIND_TO_INFO[classes.first]
150 when :info_long
151 classes.reverse.map { |kind| TOKEN_KIND_TO_INFO[kind] }.join('/')
152 when :debug
153 classes.inspect
154 end
155 title ? " title=\"#{title}\"" : ''
156 end
157
158 def setup options
159 super
160
161 @HTML_ESCAPE = HTML_ESCAPE.dup
162 @HTML_ESCAPE["\t"] = ' ' * options[:tab_width]
163
164 @opened = [nil]
165 @css = CSS.new options[:style]
166
167 hint = options[:hint]
168 if hint and not [:debug, :info, :info_long].include? hint
169 raise ArgumentError, "Unknown value %p for :hint; \
170 expected :info, :debug, false, or nil." % hint
171 end
172
173 case options[:css]
174
175 when :class
176 @css_style = Hash.new do |h, k|
177 c = CodeRay::Tokens::ClassOfKind[k.first]
178 if c == :NO_HIGHLIGHT and not hint
179 h[k.dup] = false
180 else
181 title = if hint
182 HTML.token_path_to_hint(hint, k[1..-1] << k.first)
183 else
184 ''
185 end
186 if c == :NO_HIGHLIGHT
187 h[k.dup] = '<span%s>' % [title]
188 else
189 h[k.dup] = '<span%s class="%s">' % [title, c]
190 end
191 end
192 end
193
194 when :style
195 @css_style = Hash.new do |h, k|
196 if k.is_a? ::Array
197 styles = k.dup
198 else
199 styles = [k]
200 end
201 type = styles.first
202 classes = styles.map { |c| Tokens::ClassOfKind[c] }
203 if classes.first == :NO_HIGHLIGHT and not hint
204 h[k] = false
205 else
206 styles.shift if TRANSPARENT_TOKEN_KINDS.include? styles.first
207 title = HTML.token_path_to_hint hint, styles
208 style = @css[*classes]
209 h[k] =
210 if style
211 '<span%s style="%s">' % [title, style]
212 else
213 false
214 end
215 end
216 end
217
218 else
219 raise ArgumentError, "Unknown value %p for :css." % options[:css]
220
221 end
222 end
223
224 def finish options
225 not_needed = @opened.shift
226 @out << '</span>' * @opened.size
227 unless @opened.empty?
228 warn '%d tokens still open: %p' % [@opened.size, @opened]
229 end
230
231 @out.extend Output
232 @out.css = @css
233 @out.numerize! options[:line_numbers], options
234 @out.wrap! options[:wrap]
235 @out.apply_title! options[:title]
236
237 super
238 end
239
240 def token text, type = :plain
241 case text
242
243 when nil
244 # raise 'Token with nil as text was given: %p' % [[text, type]]
245
246 when String
247 if text =~ /#{HTML_ESCAPE_PATTERN}/o
248 text = text.gsub(/#{HTML_ESCAPE_PATTERN}/o) { |m| @HTML_ESCAPE[m] }
249 end
250 @opened[0] = type
251 if text != "\n" && style = @css_style[@opened]
252 @out << style << text << '</span>'
253 else
254 @out << text
255 end
256
257
258 # token groups, eg. strings
259 when :open
260 @opened[0] = type
261 @out << (@css_style[@opened] || '<span>')
262 @opened << type
263 when :close
264 if @opened.empty?
265 # nothing to close
266 else
267 if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
268 raise 'Malformed token stream: Trying to close a token (%p) \
269 that is not open. Open are: %p.' % [type, @opened[1..-1]]
270 end
271 @out << '</span>'
272 @opened.pop
273 end
274
275 # whole lines to be highlighted, eg. a deleted line in a diff
276 when :begin_line
277 @opened[0] = type
278 if style = @css_style[@opened]
279 if style['class="']
280 @out << style.sub('class="', 'class="line ')
281 else
282 @out << style.sub('>', ' class="line">')
283 end
284 else
285 @out << '<span class="line">'
286 end
287 @opened << type
288 when :end_line
289 if @opened.empty?
290 # nothing to close
291 else
292 if $CODERAY_DEBUG and (@opened.size == 1 or @opened.last != type)
293 raise 'Malformed token stream: Trying to close a line (%p) \
294 that is not open. Open are: %p.' % [type, @opened[1..-1]]
295 end
296 @out << '</span>'
297 @opened.pop
298 end
299
300 else
301 raise 'unknown token kind: %p' % [text]
302
303 end
304 end
305
306 end
307
308 end
309 end