@@ -126,22 +126,21 @@ class HTML < Encoder
126
126
127
127
protected
128
128
129
- HTML_ESCAPE = { #:nodoc:
130
- '&' => '&' ,
131
- '"' => '"' ,
132
- '>' => '>' ,
133
- '<' => '<' ,
134
- }
129
+ def self . make_html_escape_hash
130
+ {
131
+ '&' => '&' ,
132
+ '"' => '"' ,
133
+ '>' => '>' ,
134
+ '<' => '<' ,
135
+ # "\t" => will be set to ' ' * options[:tab_width] during setup
136
+ } . tap do |hash |
137
+ # Escape ASCII control codes except \x9 == \t and \xA == \n.
138
+ ( Array ( 0x00 ..0x8 ) + Array ( 0xB ..0x1F ) ) . each { |invalid | hash [ invalid . chr ] = ' ' }
139
+ end
140
+ end
135
141
136
- # This was to prevent illegal HTML.
137
- # Strange chars should still be avoided in codes.
138
- evil_chars = Array ( 0x00 ...0x20 ) - [ ?\n, ?\t, ?\s]
139
- evil_chars . each { |i | HTML_ESCAPE [ i . chr ] = ' ' }
140
- #ansi_chars = Array(0x7f..0xff)
141
- #ansi_chars.each { |i| HTML_ESCAPE[i.chr] = '&#%d;' % i }
142
- # \x9 (\t) and \xA (\n) not included
143
- #HTML_ESCAPE_PATTERN = /[\t&"><\0-\x8\xB-\x1f\x7f-\xff]/
144
- HTML_ESCAPE_PATTERN = /[\t "&><\0 -\x8 \xB -\x1f ]/
142
+ HTML_ESCAPE = make_html_escape_hash
143
+ HTML_ESCAPE_PATTERN = /[\t "&><\0 -\x8 \xB -\x1F ]/
145
144
146
145
TOKEN_KIND_TO_INFO = Hash . new do |h , kind |
147
146
h [ kind ] = kind . to_s . gsub ( /_/ , ' ' ) . gsub ( /\b \w / ) { $&. capitalize }
@@ -172,59 +171,22 @@ def self.token_path_to_hint hint, kinds
172
171
def setup options
173
172
super
174
173
174
+ check_options! options
175
+
175
176
if options [ :wrap ] || options [ :line_numbers ]
176
177
@real_out = @out
177
178
@out = ''
178
179
end
179
180
180
- options [ :break_lines ] = true if options [ :line_numbers ] == :inline
181
-
182
181
@break_lines = ( options [ :break_lines ] == true )
183
182
184
- @HTML_ESCAPE = HTML_ESCAPE . dup
185
- @HTML_ESCAPE [ "\t " ] = ' ' * options [ :tab_width ]
183
+ @HTML_ESCAPE = HTML_ESCAPE . merge ( "\t " => ' ' * options [ :tab_width ] )
186
184
187
185
@opened = [ ]
188
186
@last_opened = nil
189
187
@css = CSS . new options [ :style ]
190
188
191
- hint = options [ :hint ]
192
- if hint && ![ :debug , :info , :info_long ] . include? ( hint )
193
- raise ArgumentError , "Unknown value %p for :hint; \
194
- expected :info, :info_long, :debug, false, or nil." % hint
195
- end
196
-
197
- css_classes = TokenKinds
198
- case options [ :css ]
199
- when :class
200
- @span_for_kind = Hash . new do |h , k |
201
- if k . is_a? ::Symbol
202
- kind = k_dup = k
203
- else
204
- kind = k . first
205
- k_dup = k . dup
206
- end
207
- if kind != :space && ( hint || css_class = css_classes [ kind ] )
208
- title = HTML . token_path_to_hint hint , k if hint
209
- css_class ||= css_classes [ kind ]
210
- h [ k_dup ] = "<span#{ title } #{ " class=\" #{ css_class } \" " if css_class } >"
211
- else
212
- h [ k_dup ] = nil
213
- end
214
- end
215
- when :style
216
- @span_for_kind = Hash . new do |h , k |
217
- kind = k . is_a? ( Symbol ) ? k : k . first
218
- h [ k . is_a? ( Symbol ) ? k : k . dup ] =
219
- if kind != :space && ( hint || css_classes [ kind ] )
220
- title = HTML . token_path_to_hint hint , k if hint
221
- style = @css . get_style Array ( k ) . map { |c | css_classes [ c ] }
222
- "<span#{ title } #{ " style=\" #{ style } \" " if style } >"
223
- end
224
- end
225
- else
226
- raise ArgumentError , "Unknown value %p for :css." % options [ :css ]
227
- end
189
+ @span_for_kinds = make_span_for_kinds ( options [ :css ] , options [ :hint ] )
228
190
229
191
@set_last_opened = options [ :hint ] || options [ :css ] == :style
230
192
end
@@ -255,20 +217,10 @@ def finish options
255
217
public
256
218
257
219
def text_token text , kind
258
- if text =~ /#{ HTML_ESCAPE_PATTERN } /o
259
- text = text . gsub ( /#{ HTML_ESCAPE_PATTERN } /o ) { |m | @HTML_ESCAPE [ m ] }
260
- end
220
+ style = @span_for_kinds [ @last_opened ? [ kind , *@opened ] : kind ]
261
221
262
- style = @span_for_kind [ @last_opened ? [ kind , *@opened ] : kind ]
263
-
264
- if @break_lines && ( i = text . index ( "\n " ) ) && ( c = @opened . size + ( style ? 1 : 0 ) ) > 0
265
- close = '</span>' * c
266
- reopen = ''
267
- @opened . each_with_index do |k , index |
268
- reopen << ( @span_for_kind [ index > 0 ? [ k , *@opened [ 0 ... index ] ] : k ] || '<span>' )
269
- end
270
- text [ i .. -1 ] = text [ i .. -1 ] . gsub ( "\n " , "#{ close } \n #{ reopen } #{ style } " )
271
- end
222
+ text = text . gsub ( /#{ HTML_ESCAPE_PATTERN } /o ) { |m | @HTML_ESCAPE [ m ] } if text =~ /#{ HTML_ESCAPE_PATTERN } /o
223
+ text = break_lines ( text , style ) if @break_lines && ( style || @opened . size > 0 ) && text . index ( "\n " )
272
224
273
225
if style
274
226
@out << style << text << '</span>'
@@ -279,25 +231,19 @@ def text_token text, kind
279
231
280
232
# token groups, eg. strings
281
233
def begin_group kind
282
- @out << ( @span_for_kind [ @last_opened ? [ kind , *@opened ] : kind ] || '<span>' )
234
+ @out << ( @span_for_kinds [ @last_opened ? [ kind , *@opened ] : kind ] || '<span>' )
283
235
@opened << kind
284
236
@last_opened = kind if @set_last_opened
285
237
end
286
238
287
239
def end_group kind
288
- if $CODERAY_DEBUG && ( @opened . empty? || @opened . last != kind )
289
- warn 'Malformed token stream: Trying to close a token (%p) ' \
290
- 'that is not open. Open are: %p.' % [ kind , @opened [ 1 ..-1 ] ]
291
- end
292
- if @opened . pop
293
- @out << '</span>'
294
- @last_opened = @opened . last if @last_opened
295
- end
240
+ check_group_nesting 'token group' , kind if $CODERAY_DEBUG
241
+ close_span
296
242
end
297
243
298
244
# whole lines to be highlighted, eg. a deleted line in a diff
299
245
def begin_line kind
300
- if style = @span_for_kind [ @last_opened ? [ kind , *@opened ] : kind ]
246
+ if style = @span_for_kinds [ @last_opened ? [ kind , *@opened ] : kind ]
301
247
if style [ 'class="' ]
302
248
@out << style . sub ( 'class="' , 'class="line ' )
303
249
else
@@ -311,16 +257,71 @@ def begin_line kind
311
257
end
312
258
313
259
def end_line kind
314
- if $CODERAY_DEBUG && ( @opened . empty? || @opened . last != kind )
315
- warn 'Malformed token stream: Trying to close a line (%p) ' \
316
- 'that is not open. Open are: %p.' % [ kind , @opened [ 1 ..-1 ] ]
260
+ check_group_nesting 'line' , kind if $CODERAY_DEBUG
261
+ close_span
262
+ end
263
+
264
+ protected
265
+
266
+ def check_options! options
267
+ unless [ false , nil , :debug , :info , :info_long ] . include? options [ :hint ]
268
+ raise ArgumentError , "Unknown value %p for :hint; expected :info, :info_long, :debug, false, or nil." % [ options [ :hint ] ]
269
+ end
270
+
271
+ unless [ :class , :style ] . include? options [ :css ]
272
+ raise ArgumentError , 'Unknown value %p for :css.' % [ options [ :css ] ]
273
+ end
274
+
275
+ options [ :break_lines ] = true if options [ :line_numbers ] == :inline
276
+ end
277
+
278
+ def css_class_for_kinds kinds
279
+ TokenKinds [ kinds . is_a? ( Symbol ) ? kinds : kinds . first ]
280
+ end
281
+
282
+ def style_for_kinds kinds
283
+ css_classes = kinds . is_a? ( Array ) ? kinds . map { |c | TokenKinds [ c ] } : [ TokenKinds [ kinds ] ]
284
+ @css . get_style_for_css_classes css_classes
285
+ end
286
+
287
+ def make_span_for_kinds method , hint
288
+ Hash . new do |h , kinds |
289
+ h [ kinds . is_a? ( Symbol ) ? kinds : kinds . dup ] = begin
290
+ css_class = css_class_for_kinds ( kinds )
291
+ title = HTML . token_path_to_hint hint , kinds if hint
292
+
293
+ if css_class || title
294
+ if method == :style
295
+ style = style_for_kinds ( kinds )
296
+ "<span#{ title } #{ " style=\" #{ style } \" " if style } >"
297
+ else
298
+ "<span#{ title } #{ " class=\" #{ css_class } \" " if css_class } >"
299
+ end
300
+ end
301
+ end
302
+ end
303
+ end
304
+
305
+ def check_group_nesting name , kind
306
+ if @opened . empty? || @opened . last != kind
307
+ warn "Malformed token stream: Trying to close a #{ name } (%p) that is not open. Open are: %p." % [ kind , @opened [ 1 ..-1 ] ]
317
308
end
309
+ end
310
+
311
+ def break_lines text , style
312
+ reopen = ''
313
+ @opened . each_with_index do |k , index |
314
+ reopen << ( @span_for_kinds [ index > 0 ? [ k , *@opened [ 0 ...index ] ] : k ] || '<span>' )
315
+ end
316
+ text . gsub ( "\n " , "#{ '</span>' * @opened . size } #{ '</span>' if style } \n #{ reopen } #{ style } " )
317
+ end
318
+
319
+ def close_span
318
320
if @opened . pop
319
321
@out << '</span>'
320
322
@last_opened = @opened . last if @last_opened
321
323
end
322
324
end
323
-
324
325
end
325
326
326
327
end
0 commit comments