ansi-sys-revived 0.8.4

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.
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: []