ansi-sys-revived 0.8.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (2) hide show
  1. data/lib/ansisys.rb +752 -0
  2. metadata +46 -0
@@ -0,0 +1,752 @@
1
+ #
2
+ # = ansisys.rb
3
+ # ANSI terminal emulator
4
+ # based on http://en.wikipedia.org/wiki/ANSI_escape_code
5
+ #
6
+ # Copyright:: Copyright (C) 2007 zunda <zunda at freeshell.org>
7
+ # License:: GPL - GPL3 or later
8
+ #
9
+ require 'webrick'
10
+ require 'nkf'
11
+
12
+ module AnsiSys
13
+ module VERSION #:nodoc:
14
+ MAJOR = 0
15
+ MINOR = 8
16
+ TINY = 4
17
+
18
+ STRING = [MAJOR, MINOR, TINY].join('.')
19
+ end
20
+
21
+ module CSSFormatter
22
+ # make a CSS style-let from a Hash of CSS settings
23
+ def hash_to_styles(hash, separator = '; ')
24
+ unless hash.empty?
25
+ return hash.map{|e| "#{e[0]}: #{e[1].join(' ')}"}.join(separator)
26
+ else
27
+ return nil
28
+ end
29
+ end
30
+ module_function :hash_to_styles
31
+ end
32
+
33
+ module Guess
34
+ # returns a $KCODE string according to guessed encoding
35
+ def self.kcode(data)
36
+ case NKF.guess(data)
37
+ when NKF::EUC
38
+ kcode = 'e'
39
+ when NKF::SJIS
40
+ kcode = 's'
41
+ when NKF::UTF8
42
+ kcode = 'u'
43
+ else
44
+ kcode = 'n'
45
+ end
46
+ return kcode
47
+ end
48
+ end
49
+
50
+ class AnsiSysError < StandardError; end
51
+
52
+ class Lexer
53
+ # Control Sequence Introducer and following code
54
+ PARAMETER_AND_LETTER = /\A([\d;]*)([[:alpha:]])/o
55
+ CODE_EQUIVALENT = {
56
+ "\r" => ['B'],
57
+ "\n" => ['E'],
58
+ }
59
+
60
+ attr_reader :buffer
61
+
62
+ # _csis_ is an Array of Code Sequence Introducers
63
+ # which can be \e[, \x9B, or both
64
+ def initialize(csis = ["\x1b["]) # CSI can also be "\x9B"
65
+ @code_start_re = Regexp.union(*(CODE_EQUIVALENT.keys + csis))
66
+ @buffer = ''
67
+ end
68
+
69
+ # add the String (clear text with some or no escape sequences) to buffer
70
+ def push(string)
71
+ @buffer += string
72
+ end
73
+
74
+ # returns array of tokens while deleting the tokenized part from buffer
75
+ def lex!
76
+ r = Array.new
77
+ @buffer.gsub!(/(?:\r\n|\n\r)/, "\n")
78
+ while @code_start_re =~ @buffer
79
+ r << [:string, $`] unless $`.empty?
80
+ if CODE_EQUIVALENT.has_key?($&)
81
+ CODE_EQUIVALENT[$&].each do |c|
82
+ r << [:code, c]
83
+ end
84
+ @buffer = $'
85
+ else
86
+ csi = $&
87
+ residual = $'
88
+ if PARAMETER_AND_LETTER =~ residual
89
+ r << [:code, $&]
90
+ @buffer = $'
91
+ else
92
+ @buffer = csi + residual
93
+ return r
94
+ end
95
+ end
96
+ end
97
+ r << [:string, @buffer] unless @buffer.empty?
98
+ @buffer = ''
99
+ return r
100
+ end
101
+ end
102
+
103
+ class Characters
104
+ # widths of characters
105
+ WIDTHS = {
106
+ "\t" => 8,
107
+ }
108
+
109
+ attr_reader :string # clear text
110
+ attr_reader :sgr # Select Graphic Rendition associated with the text
111
+
112
+ def initialize(string, sgr)
113
+ @string = string
114
+ @sgr = sgr
115
+ end
116
+
117
+ # echo the string onto the _screen_ with initial cursor as _cursor_
118
+ # _cursor_ position will be changed as the string is echoed
119
+ def echo_on(screen, cursor, kcode = nil)
120
+ each_char(kcode) do |c|
121
+ w = width(c)
122
+ cursor.fit!(w)
123
+ screen.write(c, w, cursor.cur_col, cursor.cur_row, @sgr.dup)
124
+ cursor.advance!(w)
125
+ end
126
+ return self
127
+ end
128
+
129
+ private
130
+ # iterator on each character
131
+ def each_char(kcode, &block)
132
+ @string.scan(Regexp.new('.', nil, kcode)).each do |c|
133
+ yield(c)
134
+ end
135
+ end
136
+
137
+ # width of a character
138
+ def width(char)
139
+ if WIDTHS.has_key?(char)
140
+ return WIDTHS[char]
141
+ end
142
+ case char.size # expecting number of bytes
143
+ when 1
144
+ return 1
145
+ else
146
+ return 2
147
+ end
148
+ end
149
+ end
150
+
151
+ class Cursor
152
+ # Escape sequence codes processed in this Class
153
+ CODE_LETTERS = %w(A B C D E F G H f)
154
+
155
+ attr_reader :cur_col # current column number (1-)
156
+ attr_reader :cur_row # current row number (1-)
157
+ attr_accessor :max_col # maximum column number
158
+ attr_accessor :max_row # maximum row number
159
+
160
+ def initialize(cur_col = 1, cur_row = 1, max_col = 80, max_row = 25)
161
+ @cur_col = cur_col
162
+ @cur_row = cur_row
163
+ @max_col = max_col
164
+ @max_row = max_row
165
+ end
166
+
167
+ # applies self an escape sequence code that ends with _letter_ as String
168
+ # and with some _pars_ as Integers
169
+ def apply_code!(letter, *pars)
170
+ case letter
171
+ when 'A'
172
+ @cur_row -= pars[0] ? pars[0] : 1
173
+ @cur_row = @max_row if @max_row and @cur_row > @max_row
174
+ when 'B'
175
+ @cur_row += pars[0] ? pars[0] : 1
176
+ @cur_row = @max_row if @max_row and @cur_row > @max_row
177
+ when 'C'
178
+ @cur_col += pars[0] ? pars[0] : 1
179
+ when 'D'
180
+ @cur_col -= pars[0] ? pars[0] : 1
181
+ when 'E'
182
+ @cur_row += pars[0] ? pars[0] : 1
183
+ @cur_col = 1
184
+ @max_row = @cur_row if @max_row and @cur_row > @max_row
185
+ when 'F'
186
+ @cur_row -= pars[0] ? pars[0] : 1
187
+ @cur_col = 1
188
+ @max_row = @cur_row if @max_row and @cur_row > @max_row
189
+ when 'G'
190
+ @cur_col = pars[0] ? pars[0] : 1
191
+ when 'H'
192
+ @cur_row = pars[0] ? pars[0] : 1
193
+ @cur_col = pars[1] ? pars[1] : 1
194
+ @max_row = @cur_row if @max_row and @cur_row > @max_row
195
+ when 'f'
196
+ @cur_row = pars[0] ? pars[0] : 1
197
+ @cur_col = pars[1] ? pars[1] : 1
198
+ @max_row = @cur_row if @max_row and @cur_row > @max_row
199
+ end
200
+ if @cur_row < 1
201
+ @cur_row = 1
202
+ end
203
+ if @cur_col < 1
204
+ @cur_col = 1
205
+ elsif @cur_col > @max_col
206
+ @cur_col = @max_col
207
+ end
208
+ return self
209
+ end
210
+
211
+ # changes current location for a character with _width_ to be echoed
212
+ def advance!(width = 1)
213
+ r = nil
214
+ @cur_col += width
215
+ if @cur_col > @max_col
216
+ line_feed!
217
+ r = "\n"
218
+ end
219
+ return r
220
+ end
221
+
222
+ # check if a character with _width_ fits within the maximum columns,
223
+ # feed a line if not
224
+ def fit!(width = 1)
225
+ r = nil
226
+ if @cur_col + width > @max_col + 1
227
+ line_feed!
228
+ r = "\n"
229
+ end
230
+ return r
231
+ end
232
+
233
+ # feed a line
234
+ def line_feed!
235
+ @cur_col = 1
236
+ @cur_row += 1
237
+ @max_row = @cur_row if @max_row and @cur_row > @max_row
238
+ end
239
+ end
240
+
241
+ # Select Graphic Rendition
242
+ class SGR
243
+ extend CSSFormatter
244
+
245
+ # Escape sequence codes processed in this Class
246
+ CODE_LETTERS = %w(m)
247
+
248
+ # :normal, :bold, or :faint
249
+ attr_reader :intensity
250
+
251
+ # :off or :on
252
+ attr_reader :italic
253
+
254
+ # :none, :single, or :double
255
+ attr_reader :underline
256
+
257
+ # :off, :slow, or :rapid
258
+ attr_reader :blink
259
+
260
+ # :positive or :negative
261
+ attr_reader :image
262
+
263
+ # :off or :on
264
+ attr_reader :conceal
265
+
266
+ # :black, :red, :green, :yellow, :blue, :magenta, :cyan, or :white
267
+ attr_reader :foreground
268
+
269
+ # :black, :red, :green, :yellow, :blue, :magenta, :cyan, or :white
270
+ attr_reader :background
271
+
272
+ def initialize
273
+ reset!
274
+ end
275
+
276
+ # true if all the attributes are same
277
+ def ==(other)
278
+ instance_variables.each do |ivar|
279
+ return false unless instance_variable_get(ivar) == other.instance_variable_get(ivar)
280
+ end
281
+ return true
282
+ end
283
+
284
+ # resets attributes
285
+ def reset!
286
+ apply_code!('m', 0)
287
+ end
288
+
289
+ # applies self an escape sequence code that ends with _letter_ as String
290
+ # and with some _pars_ as Integers
291
+ def apply_code!(letter = 'm', *pars)
292
+ raise AnsiSysError, "Invalid code for SGR: #{letter.inspect}" unless 'm' == letter
293
+ pars = [0] unless pars
294
+ pars.each do |code|
295
+ case code
296
+ when 0
297
+ @intensity = :normal
298
+ @italic = :off
299
+ @underline = :none
300
+ @blink = :off
301
+ @image = :positive
302
+ @conceal = :off
303
+ @foreground = :white
304
+ @background = :black
305
+ when 1..28
306
+ apply_code_table!(code)
307
+ when 30..37
308
+ @foreground = COLOR[code - 30]
309
+ @intensity = :normal
310
+ when 39
311
+ reset!
312
+ when 40..47
313
+ @background = COLOR[code - 40]
314
+ @intensity = :normal
315
+ when 49
316
+ reset!
317
+ when 90..97
318
+ @foreground = COLOR[code - 90]
319
+ @intensity = :bold
320
+ when 99
321
+ reset!
322
+ when 100..107
323
+ @background = COLOR[code - 100]
324
+ @intensity = :bold
325
+ when 109
326
+ reset!
327
+ else
328
+ raise AnsiSysError, "Invalid SGR code #{code.inspect}" unless CODE.has_key?(code)
329
+ end
330
+ end
331
+ return self
332
+ end
333
+
334
+ # renders self as :html or :text _format_ - makes a <span> html scriptlet.
335
+ # _colors_ can be Screen.default_css_colors(_inverted_, _bright_).
336
+ def render(format = :html, position = :prefix, colors = Screen.default_css_colors)
337
+ case format
338
+ when :html
339
+ case position
340
+ when :prefix
341
+ style_code = css_style(colors)
342
+ if style_code
343
+ return %Q|<span style="#{style_code}">|
344
+ else
345
+ return ''
346
+ end
347
+ when :postfix
348
+ style_code = css_style(colors)
349
+ if style_code
350
+ return '</span>'
351
+ else
352
+ return ''
353
+ end
354
+ end
355
+ when :text
356
+ return ''
357
+ end
358
+ end
359
+
360
+ # CSS stylelet
361
+ def css_style(colors = Screen.default_css_colors)
362
+ return CSSFormatter.hash_to_styles(css_styles(colors))
363
+ end
364
+
365
+ # a Hash of CSS stylelet
366
+ def css_styles(colors = Screen.default_css_colors)
367
+ r = Hash.new{|h, k| h[k] = Array.new}
368
+ # intensity is not (yet) implemented
369
+ r['font-style'] << 'italic' if @italic == :on
370
+ r['text-decoration'] << 'underline' unless @underline == :none
371
+ r['text-decoration'] << 'blink' unless @blink == :off
372
+ case @image
373
+ when :positive
374
+ fg = @foreground
375
+ bg = @background
376
+ when :negative
377
+ fg = @background
378
+ bg = @foreground
379
+ end
380
+ fg = bg if @conceal == :on
381
+ r['color'] << colors[@intensity][fg] unless fg == :white
382
+ r['background-color'] << colors[@intensity][bg] unless bg == :black
383
+ return r
384
+ end
385
+
386
+ private
387
+ def apply_code_table!(code)
388
+ raise AnsiSysError, "Invalid SGR code #{code.inspect}" unless CODE.has_key?(code)
389
+ ivar, value = CODE[code]
390
+ instance_variable_set("@#{ivar}", value)
391
+ return self
392
+ end
393
+
394
+ CODE = {
395
+ 1 => [:intensity, :bold],
396
+ 2 => [:intensity, :faint],
397
+ 3 => [:italic, :on],
398
+ 4 => [:underline, :single],
399
+ 5 => [:blink, :slow],
400
+ 6 => [:blink, :rapid],
401
+ 7 => [:image, :negative],
402
+ 8 => [:conceal, :on],
403
+ 21 => [:underline, :double],
404
+ 22 => [:intensity, :normal],
405
+ 24 => [:underline, :none],
406
+ 25 => [:blink, :off],
407
+ 27 => [:image, :positive],
408
+ 28 => [:conceal, :off],
409
+ } # :nodoc:
410
+
411
+ COLOR = {
412
+ 0 => :black,
413
+ 1 => :red,
414
+ 2 => :green,
415
+ 3 => :yellow,
416
+ 4 => :blue,
417
+ 5 => :magenta,
418
+ 6 => :cyan,
419
+ 7 => :white,
420
+ } # :nodoc:
421
+
422
+ end
423
+
424
+ class Screen
425
+ # Escape sequence codes processed in this Class
426
+ CODE_LETTERS = %w() # :nodoc:
427
+
428
+ def self.default_foreground; :white; end
429
+ def self.default_background; :black; end
430
+
431
+ # a Hash of color names for each intensity
432
+ def self.default_css_colors(inverted = false, bright = false)
433
+ r = {
434
+ :normal => {
435
+ :black => 'black',
436
+ :red => 'maroon',
437
+ :green => 'green',
438
+ :yellow => 'olive',
439
+ :blue => 'navy',
440
+ :magenta => 'purple',
441
+ :cyan => 'teal',
442
+ :white => 'silver',
443
+ },
444
+ :bold => {
445
+ :black => 'gray',
446
+ :red => 'red',
447
+ :green => 'lime',
448
+ :yellow => 'yellow',
449
+ :blue => 'blue',
450
+ :magenta => 'fuchsia',
451
+ :cyan => 'cyan',
452
+ :white => 'white'
453
+ },
454
+ :faint => {
455
+ :black => 'black',
456
+ :red => 'maroon',
457
+ :green => 'green',
458
+ :yellow => 'olive',
459
+ :blue => 'navy',
460
+ :magenta => 'purple',
461
+ :cyan => 'teal',
462
+ :white => 'silver',
463
+ },
464
+ }
465
+
466
+ if bright
467
+ r[:bold][:black] = 'black'
468
+ [:normal, :faint].each do |i|
469
+ r[i] = r[:bold]
470
+ end
471
+ end
472
+
473
+ if inverted
474
+ r.each_key do |i|
475
+ r[i][:black], r[i][:white] = r[i][:white], r[i][:black]
476
+ end
477
+ end
478
+
479
+ return r
480
+ end
481
+
482
+ # a Hash of CSS stylelet to be used in <head>
483
+ def self.css_styles(colors = Screen.default_css_colors, max_col = nil, max_row = nil)
484
+ h = {
485
+ 'color' => [colors[:normal][:white]],
486
+ 'background-color' => [colors[:normal][:black]],
487
+ 'padding' => ['0.5em'],
488
+ }
489
+ h['width'] = ["#{Float(max_col)/2}em"] if max_col
490
+ #h['height'] = ["#{max_row}em"] if max_row # could not find appropriate unit
491
+ return h
492
+ end
493
+
494
+ # CSS stylelet to be used in <head>.
495
+ # Takes the same arguments as Screen::css_styles().
496
+ def self.css_style(*args)
497
+ return "pre.screen {\n\t" + CSSFormatter.hash_to_styles(self.css_styles(*args), ";\n\t") + ";\n}\n"
498
+ end
499
+
500
+ # a Hash of keys as rows,
501
+ # which each value a Hash of keys columns and each value as
502
+ # an Array of character, its width, and associated SGR
503
+ attr_reader :lines
504
+
505
+ # a Screen
506
+ def initialize(colors = Screen.default_css_colors, max_col = nil, max_row = nil)
507
+ @colors = colors
508
+ @max_col = max_col
509
+ @max_row = max_row
510
+ @lines = Hash.new{|hash, key| hash[key] = Hash.new}
511
+ end
512
+
513
+ # CSS stylelet to be used in <head>
514
+ def css_style
515
+ self.class.css_style(@colors, @max_col, @max_row)
516
+ end
517
+
518
+ # register the _char_ at a specific location on Screen
519
+ def write(char, char_width, col, row, sgr)
520
+ @lines[Integer(row)][Integer(col)] = [char, char_width, sgr.dup]
521
+ end
522
+
523
+ # render the characters into :html or :text
524
+ # Class name in CSS can be specified as _css_class_.
525
+ # Additional stylelet can be specified as _css_style_.
526
+ def render(format = :html, css_class = 'screen', css_style = nil)
527
+ result = case format
528
+ when :text
529
+ ''
530
+ when :html
531
+ %Q|<pre#{css_class ? %Q[ class="#{css_class}"] : ''}#{css_style ? %Q| style="#{css_style}"| : ''}>\n|
532
+ else
533
+ raise AnsiSysError, "Invalid format option to render: #{format.inspect}"
534
+ end
535
+
536
+ unless @lines.keys.empty?
537
+ prev_sgr = nil
538
+ max_row = @lines.keys.max
539
+ (1..max_row).each do |row|
540
+ if @lines.has_key?(row) and not @lines[row].keys.empty?
541
+ col = 1
542
+ while col <= @lines[row].keys.max
543
+ if @lines[row].has_key?(col) and @lines[row][col]
544
+ char, width, sgr = @lines[row][col]
545
+ if prev_sgr != sgr
546
+ result += prev_sgr.render(format, :postfix, @colors) if prev_sgr
547
+ result += sgr.render(format, :prefix, @colors)
548
+ prev_sgr = sgr
549
+ end
550
+ case format
551
+ when :text
552
+ result += char
553
+ when :html
554
+ result += WEBrick::HTMLUtils.escape(char)
555
+ end
556
+ col += width
557
+ else
558
+ result += ' '
559
+ col += 1
560
+ end
561
+ end
562
+ end
563
+ result += "\n" if row < max_row
564
+ end
565
+ result += prev_sgr.render(format, :postfix, @colors) if prev_sgr
566
+ end
567
+
568
+ result += case format
569
+ when :text
570
+ ''
571
+ when :html
572
+ '</pre>'
573
+ end
574
+ return result
575
+ end
576
+
577
+ # applies self an escape sequence code that ends with _letter_ as String
578
+ # and with some _pars_ as Integers
579
+ def apply_code!(letter, *pars)
580
+ return self
581
+ end # :nodoc:
582
+ end
583
+
584
+ class Terminal
585
+ # Escape sequence codes processed in this Class
586
+ CODE_LETTERS = %w(J K S T n s u)
587
+
588
+ # _csis_ is an Array of Code Sequence Introducers
589
+ # which can be \e[, \x9B, or both
590
+ def initialize(csis = ["\x1b["])
591
+ @lexer = Lexer.new(csis)
592
+ @stream = Array.new
593
+ end
594
+
595
+ # echoes _data_, a String of characters or escape sequences
596
+ # to the Terminal.
597
+ # This method actually just buffers the echoed data.
598
+ def echo(data)
599
+ @lexer.push(data)
600
+ return self
601
+ end
602
+
603
+ # CSS stylelet to be used in <head>
604
+ def css_style(format = :html, max_col = 80, max_row = nil, colors = Screen.default_css_colors)
605
+ case format
606
+ when :html
607
+ Screen.css_style(colors, max_col, max_row)
608
+ when :text
609
+ ''
610
+ end
611
+ end
612
+
613
+ # renders the echoed data as _format_ of :html or :text.
614
+ # _max_col_, _max_row_ can be specified as Integer.
615
+ # _colors_ can be Screen.default_css_colors(_inverted_, _bright_).
616
+ def render(format = :html, max_col = 80, max_row = nil, colors = Screen.default_css_colors, css_class = nil, css_style = nil, kcode = nil)
617
+ css_class ||= 'screen'
618
+ kcode ||= Guess.kcode(@lexer.buffer)
619
+ screens = populate(format, max_col, max_row, colors, kcode)
620
+ separator = case format
621
+ when :html
622
+ "\n"
623
+ when :text
624
+ "\n---\n"
625
+ end
626
+ return screens.map{|screen| screen.render(format, css_class, css_style)}.join(separator)
627
+ end
628
+
629
+ # applies self an escape sequence code that ends with _letter_ as String
630
+ # and with some _pars_ as Integers
631
+ def apply_code!(letter, *pars)
632
+ case letter
633
+ when 'J'
634
+ cur_col = @cursor.cur_col
635
+ cur_row = @cursor.cur_row
636
+ lines = @screens[-1].lines
637
+ if pars.empty? or 0 == pars[0]
638
+ rs = lines.keys.select{|r| r > cur_row}
639
+ cs = lines[cur_row].keys.select{|c| c >= cur_col}
640
+ elsif 1 == pars[0]
641
+ rs = lines.keys.select{|r| r < cur_row}
642
+ cs = lines[cur_row].keys.select{|c| c <= cur_col}
643
+ elsif 2 == pars[0]
644
+ rs = lines.keys
645
+ cs = []
646
+ @cursor.apply_code!('H', 1, 1)
647
+ end
648
+ rs.each do |r|
649
+ lines.delete(r)
650
+ end
651
+ cs.each do |c|
652
+ lines[cur_row].delete(c)
653
+ end
654
+ when 'K'
655
+ cur_col = @cursor.cur_col
656
+ cur_row = @cursor.cur_row
657
+ line = @screens[-1].lines[cur_row]
658
+ if pars.empty? or 0 == pars[0]
659
+ cs = line.keys.select{|c| c >= cur_col}
660
+ elsif 1 == pars[0]
661
+ cs = line.keys.select{|c| c <= cur_col}
662
+ elsif 2 == pars[0]
663
+ cs = line.keys
664
+ end
665
+ cs.each do |c|
666
+ line.delete(c)
667
+ end
668
+ when 'S'
669
+ lines = @screens[-1].lines
670
+ n = pars.empty? ? 1 : pars[0]
671
+ n.times do |l|
672
+ lines.delete(l)
673
+ end
674
+ rs = lines.keys.sort
675
+ rs.each do |r|
676
+ lines[r-n] = lines[r]
677
+ lines.delete(r)
678
+ end
679
+ @cursor.apply_code!('H', rs[-1] - n + 1, 1)
680
+ when 'T'
681
+ lines = @screens[-1].lines
682
+ n = pars.empty? ? 1 : pars[0]
683
+ rs = lines.keys.sort_by{|a| -a} # sort.reverse
684
+ rs.each do |r|
685
+ lines[r+n] = lines[r]
686
+ lines.delete(r)
687
+ end
688
+ @cursor.apply_code!('H', rs[-1] - n + 1, 1)
689
+ when 's'
690
+ @stored_cursor = @cursor.dup
691
+ when 'u'
692
+ @cursor = @stored_cursor.dup if @stored_cursor
693
+ end
694
+
695
+ return self
696
+ end
697
+
698
+ private
699
+ def populate(format = :html, max_col = 80, max_row = nil, colors = Screen.default_css_colors, kcode = nil)
700
+ @cursor = Cursor.new(1, 1, max_col, max_row)
701
+ @stored_cursor = nil
702
+ @screens = [Screen.new(colors, max_col, max_row)]
703
+ @sgr = SGR.new
704
+ @stream += @lexer.lex!
705
+ @stream.each do |type, payload|
706
+ case type
707
+ when :string
708
+ Characters.new(payload, @sgr).echo_on(@screens[-1], @cursor, kcode)
709
+ when :code
710
+ unless Lexer::PARAMETER_AND_LETTER =~ payload
711
+ raise AnsiSysError, "Invalid code: #{payload.inspect}"
712
+ end
713
+ letter = $2
714
+ pars = $1.split(/;/).map{|i| i.to_i}
715
+ applied = false
716
+ [@sgr, @cursor, @screens[-1], self].each do |recv|
717
+ if recv.class.const_get(:CODE_LETTERS).include?(letter)
718
+ recv.apply_code!(letter, *pars)
719
+ applied = true
720
+ end
721
+ end
722
+ raise AnsiSysError, "Invalid code or not implemented: #{payload.inspect}" unless applied
723
+ end
724
+ end
725
+ return @screens
726
+ end
727
+ end
728
+ end
729
+
730
+ if defined?(Hiki) and Hiki::Plugin == self.class
731
+ # a Hiki plugin method to render a file of text with ANSI escape sequences.
732
+ # Attached file name should be specified as _file_name_.
733
+ # _max_row_ can be specified.
734
+ # _invert_ and _bright_ can be specified to change colors.
735
+ # _page_ can be specified to show a file attached to another page.
736
+ def ansi_screen(file_name, max_col = 80, invert = false, bright = true, page = @page)
737
+ return '' unless file_name =~ /\.(txt|rd|rb|c|pl|py|sh|java|html|htm|css|xml|xsl|sql|yaml)\z/i
738
+ page_file_name = "#{page.untaint.escape}/#{file_name.untaint.escape}"
739
+ path = "#{@conf.cache_path}/attach/#{page_file_name}"
740
+ unless File.exists?(path)
741
+ raise PluginError, "No such file:#{page_file_name}"
742
+ end
743
+ data = File.open(path){|f| f.read}.to_euc
744
+
745
+ colors = AnsiSys::Screen.default_css_colors(invert, bright)
746
+ styles = AnsiSys::CSSFormatter.hash_to_styles(AnsiSys::Screen.css_styles(colors, max_col, nil), '; ')
747
+
748
+ terminal = AnsiSys::Terminal.new
749
+ terminal.echo(data)
750
+ return terminal.render(:html, max_col, nil, colors, 'screen', styles, 'e') + "\n"
751
+ end
752
+ end
metadata ADDED
@@ -0,0 +1,46 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ansi-sys-revived
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.8.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Damian Nowak
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-01 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email: nowaker@pacmanvps.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/ansisys.rb
21
+ homepage: https://github.com/Nowaker/ruby-ansi-sys-revived
22
+ licenses:
23
+ - GNU GPL v3
24
+ post_install_message:
25
+ rdoc_options: []
26
+ require_paths:
27
+ - lib
28
+ required_ruby_version: !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.9.3
34
+ required_rubygems_version: !ruby/object:Gem::Requirement
35
+ none: false
36
+ requirements:
37
+ - - ! '>='
38
+ - !ruby/object:Gem::Version
39
+ version: 1.8.11
40
+ requirements: []
41
+ rubyforge_project:
42
+ rubygems_version: 1.8.29
43
+ signing_key:
44
+ specification_version: 3
45
+ summary: A library to render texts with ANSI escape sequences. Revived from RubyForge.
46
+ test_files: []