escape_code 0.2

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