escape_code 0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d91dddd55b34e7ef953ad2a19b94d433e5cb18c1
4
+ data.tar.gz: 5d85e224ed28b54e21b4104eb4f1cad42c2f925b
5
+ SHA512:
6
+ metadata.gz: 9c141acd971f8299d224fba4f24dfc558ec6742a10537c90771145ec0c2a03ed201cb53ed1bd395cc60633c90b02ea5d27a719b78970a4f98983b185d679ef21
7
+ data.tar.gz: 28c947714e0fb3f6c2544235c249bb41f6b21019e5166843304c1ed54aace1812f094515b48a37f17404fc799d448a658ed6ba18d80e623600f7ec595c56eb64
@@ -0,0 +1,9 @@
1
+ module EscapeCode
2
+ end
3
+
4
+ require 'escape_code/code'
5
+ require 'escape_code/html_formatter'
6
+ require 'escape_code/html_formatter/color_scheme'
7
+ require 'escape_code/scanner'
8
+ require 'escape_code/sgr_command'
9
+ require 'escape_code/sgr_state'
@@ -0,0 +1,36 @@
1
+
2
+ class EscapeCode::Code
3
+ REGEX = /\e\[([0-9;]*)([a-zA-Z])/
4
+
5
+ SGR = 'm'
6
+
7
+ # TODO: Support other types of escape codes, like private sequences and non-alphanumeric mode characters
8
+ attr_reader :type, :args, :sgr_commands
9
+
10
+ def initialize(type, args)
11
+ @type = type
12
+ @args = args
13
+
14
+ if sgr?
15
+ @sgr_commands ||= begin
16
+ if args == []
17
+ # SGR without an argument is equvalent to reset
18
+ [EscapeCode::SgrCommand.new(SgrCommand::RESET)]
19
+ else
20
+ args.map do |arg|
21
+ EscapeCode::SgrCommand.new(arg)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def self.parse(code)
29
+ raise 'not a valid escape sequence' unless code =~ REGEX
30
+ new($~[2], $~[1].split(';'))
31
+ end
32
+
33
+ def sgr?
34
+ type == SGR
35
+ end
36
+ end
@@ -0,0 +1,43 @@
1
+ require 'cgi'
2
+
3
+ class EscapeCode::HtmlFormatter
4
+ def initialize(text, prefix = '', color_scheme: EscapeCode::HtmlFormatter::ColorScheme.default)
5
+ @prefix = prefix
6
+ @color_scheme = color_scheme
7
+ @scanner = EscapeCode::Scanner.new(text)
8
+ end
9
+
10
+ def generate_stylesheet
11
+ @color_scheme.generate_stylesheet
12
+ end
13
+
14
+ def generate
15
+ state = EscapeCode::SgrState.new
16
+
17
+ @scanner.scan.map do |thing|
18
+ case thing
19
+ when EscapeCode::Code
20
+ state.ingest(thing)
21
+ when String
22
+ classes = compute_classes(state)
23
+ if classes.empty?
24
+ CGI.escapeHTML(thing)
25
+ else
26
+ "<span class='#{compute_classes(state)}'>#{CGI.escapeHTML(thing)}</span>"
27
+ end
28
+ end
29
+ end.compact.join
30
+ end
31
+
32
+ def generate_page
33
+ "<html><head><style>#{generate_stylesheet}</style></head><body><pre>#{generate}</pre></body></html>"
34
+ end
35
+
36
+ private def compute_classes(state)
37
+ classes = []
38
+ classes << 'bold' if state.bold?
39
+ classes << "#{@prefix}#{state.foreground}-foreground" if state.foreground
40
+ classes << "#{@prefix}#{state.background}-background" if state.background
41
+ classes.join(' ')
42
+ end
43
+ end
@@ -0,0 +1,49 @@
1
+
2
+ class EscapeCode::HtmlFormatter::ColorScheme
3
+ def self.default
4
+ @default ||= new(
5
+ foreground: {
6
+ black: '#2e3436',
7
+ red: '#cc0000',
8
+ green: '#4e9a06',
9
+ yellow: '#c4a000',
10
+ blue: '#3465a4',
11
+ purple: '#75507b',
12
+ cyan: '#06989a',
13
+ white: '#d3d7cf'
14
+ },
15
+ background: {
16
+ black: '#555753',
17
+ red: '#ef2929',
18
+ green: '#8ae234',
19
+ yellow: '#fce94f',
20
+ blue: '#729fcf',
21
+ purple: '#ad7fa8',
22
+ cyan: '#34e2e2',
23
+ white: '#eeeeec'
24
+ }
25
+ )
26
+ end
27
+
28
+ def initialize(foreground:, background: nil)
29
+ @foreground = foreground
30
+ @background = background || foreground
31
+ end
32
+
33
+ def generate_stylesheet(prefix = '')
34
+ stylesheet = []
35
+
36
+ @background.each do |color, value|
37
+ stylesheet << ".#{prefix}#{color}-background {\n background-color: #{value};\n}"
38
+ end
39
+
40
+ @foreground.each do |color, value|
41
+ stylesheet << ".#{prefix}#{color}-foreground {\n color: #{value};\n}"
42
+ end
43
+
44
+ # TODO: Do this in a better way
45
+ stylesheet << ".#{prefix}bold {\n font-weight: bold;\n}"
46
+
47
+ stylesheet.join("\n")
48
+ end
49
+ end
@@ -0,0 +1,29 @@
1
+ require 'strscan'
2
+
3
+ class EscapeCode::Scanner
4
+ def initialize(string)
5
+ @string = string
6
+ end
7
+
8
+ def scan(&block)
9
+ enumerator = Enumerator.new do |y|
10
+ scanner = StringScanner.new(@string)
11
+
12
+ until scanner.eos?
13
+ if scanner.scan EscapeCode::Code::REGEX
14
+ y << EscapeCode::Code.parse(scanner.matched)
15
+ else
16
+ # TODO: don't split up strings with \e but not an actual escape sequence
17
+ y << scanner.scan(/[^\e]*/)
18
+ end
19
+ end
20
+ end
21
+
22
+ if block
23
+ enumerator.each(&block)
24
+ nil
25
+ else
26
+ enumerator
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,44 @@
1
+
2
+ class EscapeCode::SgrCommand
3
+ RESET = '0'
4
+ BOLD = '1'
5
+
6
+ COLORS = {
7
+ '0' => :black,
8
+ '1' => :red,
9
+ '2' => :green,
10
+ '3' => :yellow,
11
+ '4' => :blue,
12
+ '5' => :purple,
13
+ '6' => :cyan,
14
+ '7' => :white
15
+ }
16
+
17
+ attr_reader :type
18
+
19
+ def initialize(type)
20
+ @type = type
21
+ end
22
+
23
+ def reset?
24
+ type == RESET
25
+ end
26
+
27
+ def bold?
28
+ type == BOLD
29
+ end
30
+
31
+ def foreground_color?
32
+ type =~ /^3[0-7]$/
33
+ end
34
+
35
+ def background_color?
36
+ type =~ /^4[0-7]/
37
+ end
38
+
39
+ def color
40
+ return nil unless foreground_color? || background_color?
41
+
42
+ EscapeCode::SgrCommand::COLORS[type[1]]
43
+ end
44
+ end
@@ -0,0 +1,31 @@
1
+
2
+ class EscapeCode::SgrState
3
+ attr_reader :bold, :foreground, :background
4
+ alias_method :bold?, :bold
5
+
6
+ def initialize
7
+ @bold = false
8
+ @foreground = nil
9
+ @background = nil
10
+ end
11
+
12
+ def ingest(command)
13
+ # convenience thing to allow passing in an entire code instead of individual SGR commands
14
+ if command.is_a?(EscapeCode::Code)
15
+ command.sgr_commands.each { |c| ingest(c) } if command.sgr?
16
+ return
17
+ end
18
+
19
+ if command.reset?
20
+ @bold = false
21
+ @foreground = nil
22
+ @background = nil
23
+ elsif command.bold?
24
+ @bold = true
25
+ elsif command.foreground_color?
26
+ @foreground = command.color
27
+ elsif command.background_color?
28
+ @background = command.color
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ require 'escape_code'
2
+
3
+ RSpec.describe EscapeCode do
4
+ it 'handles pathological cases' do
5
+ result = EscapeCode::HtmlFormatter.new("one \e[1mtwo\e[0m three \e[42;37mfour \e[1mfive \e[41msix\e[0m").generate
6
+ expected = "one <span class='bold'>two</span> three <span class='white-foreground green-background'>four </span><span class='bold white-foreground green-background'>five </span><span class='bold white-foreground red-background'>six</span>"
7
+ expect(result).to eq(expected)
8
+ end
9
+
10
+ it 'generates stylesheets that have both background and foreground colors' do
11
+ stylesheet = EscapeCode::HtmlFormatter::ColorScheme.default.generate_stylesheet
12
+ expect(stylesheet).to match(/ background-color:/)
13
+ expect(stylesheet).to match(/ color:/)
14
+ end
15
+
16
+ it 'should probably have more specs'
17
+ end
metadata ADDED
@@ -0,0 +1,51 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: escape_code
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.2'
5
+ platform: ruby
6
+ authors:
7
+ - Alex Boyd
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-19 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: alex@opengroove.org
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/escape_code.rb
20
+ - lib/escape_code/code.rb
21
+ - lib/escape_code/html_formatter.rb
22
+ - lib/escape_code/html_formatter/color_scheme.rb
23
+ - lib/escape_code/scanner.rb
24
+ - lib/escape_code/sgr_command.rb
25
+ - lib/escape_code/sgr_state.rb
26
+ - spec/escape_code_spec.rb
27
+ homepage: https://github.com/javawizard/escape_code
28
+ licenses: []
29
+ metadata: {}
30
+ post_install_message:
31
+ rdoc_options: []
32
+ require_paths:
33
+ - lib
34
+ required_ruby_version: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - ">="
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ required_rubygems_version: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ requirements: []
45
+ rubyforge_project:
46
+ rubygems_version: 2.2.2
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: ANSI escape code parsing library and ANSI -> HTML converter
50
+ test_files: []
51
+ has_rdoc: