pry-theme 0.1.3 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +1 -0
  3. data/.travis.yml +8 -1
  4. data/CHANGELOG.md +40 -0
  5. data/Gemfile +1 -1
  6. data/README.md +26 -48
  7. data/Rakefile +10 -1
  8. data/VERSION +1 -0
  9. data/lib/pry-theme/basic_editor.rb +116 -0
  10. data/lib/pry-theme/color.rb +431 -0
  11. data/lib/pry-theme/color_table.rb +39 -0
  12. data/lib/pry-theme/colors/color16.rb +35 -0
  13. data/lib/pry-theme/colors/color256.rb +30 -0
  14. data/lib/pry-theme/colors/color8.rb +31 -0
  15. data/lib/pry-theme/commands.rb +237 -275
  16. data/lib/pry-theme/declaration.rb +120 -0
  17. data/lib/pry-theme/definition.rb +111 -0
  18. data/lib/pry-theme/formattable.rb +26 -0
  19. data/lib/pry-theme/hex.rb +76 -0
  20. data/lib/pry-theme/preview.rb +74 -0
  21. data/lib/pry-theme/rgb.rb +238 -13
  22. data/lib/pry-theme/term.rb +66 -0
  23. data/lib/pry-theme/theme.rb +116 -26
  24. data/lib/pry-theme/theme_list.rb +52 -0
  25. data/lib/pry-theme/when_started_hook.rb +25 -27
  26. data/lib/pry-theme.rb +84 -158
  27. data/pry-theme.gemspec +14 -18
  28. data/spec/color_table.rb +53 -0
  29. data/spec/colors/color16_spec.rb +255 -0
  30. data/spec/colors/color256_spec.rb +323 -0
  31. data/spec/colors/color8_spec.rb +254 -0
  32. data/spec/commands_spec.rb +203 -0
  33. data/spec/helper.rb +16 -0
  34. data/spec/hex_spec.rb +52 -0
  35. data/spec/rgb_spec.rb +81 -0
  36. data/spec/term_spec.rb +23 -0
  37. data/spec/theme_spec.rb +486 -0
  38. data/themes/github.prytheme.rb +49 -0
  39. data/themes/monokai.prytheme.rb +48 -0
  40. data/themes/pry-classic-16.prytheme.rb +48 -0
  41. data/themes/pry-classic-256.prytheme.rb +48 -0
  42. data/themes/pry-classic-8.prytheme.rb +48 -0
  43. data/themes/pry-cold.prytheme.rb +49 -0
  44. data/themes/pry-love-16.prytheme.rb +48 -0
  45. data/themes/pry-love-8.prytheme.rb +48 -0
  46. data/themes/pry-modern-16.prytheme.rb +48 -0
  47. data/themes/pry-modern-256.prytheme.rb +48 -0
  48. data/themes/pry-modern-8.prytheme.rb +48 -0
  49. data/themes/pry-monochrome.prytheme.rb +32 -0
  50. data/themes/pry-siberia-16.prytheme.rb +48 -0
  51. data/themes/pry-siberia-8.prytheme.rb +48 -0
  52. data/themes/pry-tepid-16.prytheme.rb +48 -0
  53. data/themes/pry-tepid-8.prytheme.rb +48 -0
  54. data/themes/pry-zealand-16.prytheme.rb +48 -0
  55. data/themes/pry-zealand-8.prytheme.rb +49 -0
  56. data/themes/railscasts.prytheme.rb +50 -0
  57. data/themes/solarized.prytheme.rb +48 -0
  58. data/themes/tomorrow.prytheme.rb +48 -0
  59. data/themes/twilight.prytheme.rb +48 -0
  60. data/themes/vim-default.prytheme.rb +50 -0
  61. data/themes/vim-detailed.prytheme.rb +50 -0
  62. data/themes/zenburn.prytheme.rb +48 -0
  63. metadata +56 -41
  64. data/lib/pry-theme/color_converter.rb +0 -55
  65. data/lib/pry-theme/helper.rb +0 -87
  66. data/lib/pry-theme/palette.rb +0 -85
  67. data/lib/pry-theme/term_notation.rb +0 -17
  68. data/lib/pry-theme/version.rb +0 -3
  69. data/test/fixtures/pry-classic.prytheme +0 -38
  70. data/test/helper.rb +0 -56
  71. data/test/test_color_converter.rb +0 -38
  72. data/test/test_commands.rb +0 -55
  73. data/test/test_helper.rb +0 -45
  74. data/test/test_palette.rb +0 -11
  75. data/themes/github.prytheme +0 -43
  76. data/themes/monokai.prytheme +0 -42
  77. data/themes/pry-classic.prytheme +0 -43
  78. data/themes/pry-cold.prytheme +0 -43
  79. data/themes/pry-modern.prytheme +0 -42
  80. data/themes/railscasts.prytheme +0 -44
  81. data/themes/saturday.prytheme +0 -42
  82. data/themes/solarized.prytheme +0 -43
  83. data/themes/tomorrow.prytheme +0 -43
  84. data/themes/twilight.prytheme +0 -42
  85. data/themes/vim-default.prytheme +0 -42
  86. data/themes/vim-detailed.prytheme +0 -42
  87. data/themes/zenburn.prytheme +0 -43
@@ -0,0 +1,120 @@
1
+ module PryTheme
2
+ class Color
3
+
4
+ # @since 0.2.0
5
+ # @api private
6
+ class Declaration
7
+
8
+ class << self
9
+ def translate(decl, color_model)
10
+ decl = Declaration.new(decl, color_model)
11
+ decl.parse
12
+ decl.to_color
13
+ end
14
+ alias_method :t, :translate
15
+ end
16
+
17
+ def initialize(color_decl, color_model)
18
+ validate_effects(color_decl, color_model)
19
+
20
+ @color_decl = color_decl
21
+ @color_model = color_model
22
+ @color_class = PryTheme.const_get(:"Color#{ color_model }")
23
+ @effects = {}
24
+ @parsed = false
25
+ end
26
+
27
+ def parse
28
+ if @parsed
29
+ return
30
+ else
31
+ case @color_decl.size
32
+ when 3 then build_from_two_layers
33
+ when 2 then build_from_two_args
34
+ when 1 then build_from_arg
35
+ end
36
+ @parsed = true
37
+ end
38
+ end
39
+
40
+ def to_color
41
+ [:readable, :hex, :rgb, :term].each do |type|
42
+ begin
43
+ return @color_class.new({
44
+ :from => type,
45
+ :foreground => @fg,
46
+ :background => @bg
47
+ }.merge!(@effects))
48
+ rescue ArgumentError, TypeError
49
+ next
50
+ end
51
+ end
52
+ raise PryTheme::ThemeError,
53
+ %|malformed color declaration (#{ [@fg, @bg].compact.join(', ') })|
54
+ end
55
+
56
+ private
57
+
58
+ def validate_effects(color_decl, color_model)
59
+ incorrect_color_model = (color_model != 256)
60
+ incorrect_declaration = (color_decl.any? do |decl|
61
+ decl.is_a?(Array) && decl.all? { |elem| elem.is_a?(Symbol) }
62
+ end)
63
+
64
+ if incorrect_color_model && incorrect_declaration
65
+ raise PryTheme::ThemeError,
66
+ 'effects are available only for 256-color themes'
67
+ end
68
+ end
69
+
70
+ def build_effects
71
+ if @color_decl.any?
72
+ @effects = @color_decl.shift.inject({}) { |h, k| h[k] = true; h }
73
+ end
74
+ end
75
+
76
+ def build_from_two_layers
77
+ @fg, @bg = 2.times.map { @color_decl.shift }
78
+ build_effects
79
+ end
80
+
81
+ def build_from_two_args
82
+ if decl_has_bg_key?
83
+ @bg = @color_decl.first[:bg]
84
+ @color_decl.shift
85
+ else
86
+ @fg = @color_decl.shift
87
+ if @color_decl.last.is_a?(Array)
88
+ @bg = @color_decl.shift if decl_contains_rgb?
89
+ else
90
+ @bg = @color_decl.shift
91
+ end
92
+ end
93
+ build_effects
94
+ end
95
+
96
+ def build_from_arg
97
+ f = @color_decl.first
98
+ if decl_has_bg_key?
99
+ @bg = f[:bg]
100
+ @color_decl.shift
101
+ elsif f.is_a?(String) || f.is_a?(Fixnum)
102
+ @fg = @color_decl.shift
103
+ else
104
+ build_effects
105
+ end
106
+ end
107
+
108
+ def decl_has_bg_key?
109
+ f = @color_decl.first
110
+ f.is_a?(Hash) && f.has_key?(:bg)
111
+ end
112
+
113
+ def decl_contains_rgb?
114
+ l = @color_decl.last
115
+ l.size == 3 && l.all? { |decl| decl.is_a?(Fixnum) }
116
+ end
117
+
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,111 @@
1
+ module PryTheme
2
+ class Theme
3
+
4
+ # @since 0.2.0
5
+ # @api private
6
+ module DynamicMethod
7
+ def def_dynamic_methods(*dynamic_methods)
8
+ dynamic_methods.each { |attr|
9
+ define_method(attr) do |*args|
10
+ name = :"@#{ attr }"
11
+ if args.first
12
+ decl =
13
+ Color::Declaration.t(args, instance_variable_get(:@color_model))
14
+ instance_variable_set(name, decl)
15
+ end
16
+ instance_variable_get(name)
17
+ end
18
+ }
19
+ end
20
+ end
21
+
22
+ # @since 0.2.0
23
+ # @api private
24
+ module DefaultAttrs
25
+ def set_default_attrs(attrs)
26
+ default_color = PryTheme.const_get(:"Color#{ @color_model }").new
27
+ attrs.each do |attr|
28
+ instance_variable_set(:"@#{ attr }", default_color.dup)
29
+ end
30
+ end
31
+
32
+ def set_nested_attrs
33
+ regexp { set_default_attrs(Definition::Regexp::ATTRS) }
34
+ shell { set_default_attrs(Definition::Shell::ATTRS) }
35
+ string { set_default_attrs(Definition::String::ATTRS) }
36
+ end
37
+
38
+ def method_missing(meth, *args, &block)
39
+ raise PryTheme::ThemeError, %|unknown option "#{ meth }"|
40
+ end
41
+ end
42
+
43
+ # @since 0.2.0
44
+ # @api private
45
+ # @todo: possibly, try to avoid duplication.
46
+ class Definition
47
+ extend DynamicMethod
48
+ include DefaultAttrs
49
+
50
+ ATTRS = [
51
+ :class_, :class_variable, :comment, :constant, :error, :float,
52
+ :global_variable, :inline_delimiter, :instance_variable, :integer,
53
+ :keyword, :method, :predefined_constant, :symbol
54
+ ]
55
+
56
+ def_dynamic_methods *ATTRS
57
+
58
+ def initialize(color_model, &block)
59
+ @color_model = color_model
60
+ set_default_attrs(ATTRS) and set_nested_attrs
61
+ instance_eval(&block)
62
+ end
63
+
64
+ def regexp(&block)
65
+ @regexp = Definition::Regexp.new(@color_model, &block) if block_given?
66
+ @regexp
67
+ end
68
+
69
+ def shell(&block)
70
+ @shell = Definition::Shell.new(@color_model, &block) if block_given?
71
+ @shell
72
+ end
73
+
74
+ def string(&block)
75
+ @string = Definition::String.new(@color_model, &block) if block_given?
76
+ @string
77
+ end
78
+
79
+ class Compound
80
+ extend DynamicMethod
81
+ include DefaultAttrs
82
+
83
+ ATTRS = [:self_, :char, :content, :delimiter, :escape]
84
+
85
+ def_dynamic_methods *ATTRS
86
+
87
+ def initialize(color_model, &block)
88
+ @color_model = color_model
89
+ set_default_attrs(ATTRS)
90
+ instance_eval(&block)
91
+ end
92
+ end
93
+
94
+ class Regexp < Compound
95
+ ATTRS = [:modifier]
96
+
97
+ def_dynamic_methods *ATTRS
98
+
99
+ def initialize(color_model, &block)
100
+ @color_model = color_model
101
+ set_default_attrs(ATTRS)
102
+ super
103
+ end
104
+ end
105
+
106
+ Shell = Class.new(Compound)
107
+ String = Class.new(Compound)
108
+ end
109
+
110
+ end
111
+ end
@@ -0,0 +1,26 @@
1
+ module PryTheme
2
+ module Formattable
3
+
4
+ FORMATTING = {
5
+ :bold => 1,
6
+ :italic => 3,
7
+ :underline => 4
8
+ }
9
+
10
+ def bold
11
+ options[:bold] && FORMATTING[:bold]
12
+ end
13
+ def bold?; !!bold; end
14
+
15
+ def italic
16
+ options[:italic] && FORMATTING[:italic]
17
+ end
18
+ def italic?; !!italic; end
19
+
20
+ def underline
21
+ options[:underline] && FORMATTING[:underline]
22
+ end
23
+ def underline?; !!underline; end
24
+
25
+ end
26
+ end
@@ -0,0 +1,76 @@
1
+ module PryTheme
2
+ # @since 0.2.0
3
+ # @api private
4
+ #
5
+ # Represents a HEX colour. It's possible to convert a HEX instance into {TERM}
6
+ # or {RGB} colours. However, this conversion is half-duplex (see {RGB}). This
7
+ # class validates its input (you won't see malformed or nonexistent HEX
8
+ # colours).
9
+ #
10
+ # @note Conversion to {TERM} relies on {RGB#to_term}, as a {HEX} instance
11
+ # converts itself to {RGB} first, and only then to {TERM}.
12
+ # @example Conversion to RGB
13
+ # HEX.new('#ffffff').to_rgb #=> (RGB: 255, 255, 255)
14
+ # @example Conversion to TERM
15
+ # HEX.new('#ffffff').to_term(16) #=> (TERM-16: 15)
16
+ #
17
+ # # Approximation.
18
+ # HEX.new('#fc33ea').to_term #=> (TERM-256: 207)
19
+ class HEX
20
+
21
+ # Represents a single HEX "digit".
22
+ BYTE = /[A-F\d]{2}/i
23
+
24
+ # A hex String must be prefixed with an octothorp. Use any letter case.
25
+ PATTERN = /\A#(#{ BYTE }){3}\z/i
26
+
27
+ # @param [String] value must be a valid hex number
28
+ def initialize(value)
29
+ validate_value(value)
30
+ @value = value
31
+ end
32
+
33
+ # @return [String]
34
+ def inspect
35
+ "(HEX: #{ @value })"
36
+ end
37
+
38
+ # @example
39
+ # HEX.new('#33aabb').to_s #=> "#33aabb"
40
+ # @return [String]
41
+ def to_s
42
+ @value
43
+ end
44
+
45
+ # Converts `self` into {RGB}.
46
+ # @return [RGB]
47
+ def to_rgb
48
+ RGB.new(@value[1..-1].scan(BYTE).map! { |b| b.to_i(16) })
49
+ end
50
+
51
+ # Converts `self` into {TERM}.
52
+ # @return [RGB]
53
+ def to_term(color_model = 256)
54
+ to_rgb.to_term(color_model)
55
+ end
56
+
57
+ private
58
+
59
+ # Validates whether +value+ is a valid hex colour value.
60
+ #
61
+ # @param [String] value
62
+ # @raise [TypeError] if +value+ isn't String
63
+ # @raise [ArgumentError] if +value+ is malformed
64
+ # @return [void]
65
+ def validate_value(value)
66
+ unless value.is_a?(String)
67
+ raise TypeError, "can't convert #{ value.class } into PryTheme::HEX"
68
+ end
69
+ if value !~ PryTheme::HEX::PATTERN
70
+ raise ArgumentError, %|invalid value for PryTheme::HEX#new(): "#{ value }"|
71
+ end
72
+ true
73
+ end
74
+
75
+ end
76
+ end
@@ -0,0 +1,74 @@
1
+ module PryTheme
2
+ class Preview
3
+
4
+ def initialize(theme)
5
+ @theme = theme
6
+ end
7
+
8
+ def short
9
+ cur_theme = ThemeList.current_theme
10
+ @theme.activate
11
+ [header, description, '--', short_snippet].join("\n")
12
+ ensure
13
+ @theme.disable
14
+ cur_theme.activate
15
+ end
16
+
17
+ def long
18
+ long_snippet
19
+ end
20
+
21
+ def description
22
+ @theme.description
23
+ end
24
+
25
+ def header
26
+ Pry::Helpers::Text.bold("#{ @theme.name } / #{ @theme.color_model }")
27
+ end
28
+
29
+ def banner(msg)
30
+ safe_width = 80
31
+ delimiter = ('-' * safe_width)
32
+ [delimiter,
33
+ Pry::Helpers::Text.bold(msg.center(safe_width)),
34
+ delimiter
35
+ ].join("\n") + "\n"
36
+ end
37
+
38
+ private
39
+
40
+ def short_snippet
41
+ code = Pry::Helpers::CommandHelpers.unindent(<<-'CODE')
42
+ 1: class Theme
43
+ 2: def method
44
+ 3: @ivar, @@cvar, lvar = 10_000, 400.00, "string"
45
+ 4: end
46
+ 5: end
47
+ CODE
48
+ Pry::Helpers::BaseHelpers.colorize_code(code)
49
+ end
50
+
51
+ def long_snippet
52
+ code = Pry::Helpers::CommandHelpers.unindent(<<-CODE)
53
+ # "#{ @theme.name }" theme.
54
+ class PryTheme::ThisIsAClass
55
+ def this_is_a_method
56
+ THIS_IS_A_CONSTANT = :this_is_a_symbol
57
+ this_is_a_local_var = "\#{this} \#@is a string.\\n"
58
+ this_is_a_float = 10_000.00
59
+ this_is_an_integer = 10_000
60
+
61
+ # TRUE and FALSE are predefined constants.
62
+ $this_is_a_global_variable = TRUE or FALSE
63
+
64
+ @this_is_an_instance_variable = `echo '\#@hi \#{system} call\\n'`
65
+ @@this_is_a_class_variable = @@@\\\\$ # An error.
66
+
67
+ /[0-9]{1,3}this \#{is} a regexp\\w+/xi
68
+ end
69
+ end
70
+ CODE
71
+ Pry::Helpers::BaseHelpers.colorize_code(code)
72
+ end
73
+ end
74
+ end
data/lib/pry-theme/rgb.rb CHANGED
@@ -1,23 +1,248 @@
1
1
  module PryTheme
2
- module RGB
3
-
4
- SYSTEM = [
5
- [0x00, 0x00, 0x00], [0x80, 0x00, 0x00], [0x00, 0x80, 0x00],
6
- [0x80, 0x80, 0x00], [0x00, 0x00, 0x80], [0x80, 0x00, 0x80],
7
- [0x00, 0x80, 0x80], [0xc0, 0xc0, 0xc0], [0x80, 0x80, 0x80],
8
- [0xff, 0x00, 0x00], [0x00, 0xff, 0x00], [0xff, 0xff, 0x00],
9
- [0x00, 0x00, 0xff], [0xff, 0x00, 0xff], [0x00, 0xff, 0xff],
10
- [0xff, 0xff, 0xff]
2
+ # @since 0.2.0
3
+ # @api private
4
+ #
5
+ # Represents an RGB colour. It's possible to convert an RGB instance into
6
+ # {HEX} or {TERM} colours. However, this conversion is half-duplex. If an RGB
7
+ # instance gets converted to {TERM} format, there is a high chance that it
8
+ # will be approximated to fit in range of colour model (colour model can be
9
+ # set via an argument of the conversion method). This class validates its
10
+ # input (you won't see malformed of nonexistent RGB colours).
11
+ #
12
+ # @example Conversion to HEX
13
+ # RGB.new([0, 0, 0]).to_hex #=> (HEX: #000000)
14
+ # @example Conversion to TERM
15
+ # RGB.new([0, 0, 0]).to_term(8) #=> (TERM-8: 0)
16
+ #
17
+ # # Approximation.
18
+ # RGB.new([254, 244, 231]).to_term(8) #=> (TERM-8: 7)
19
+ class RGB
20
+
21
+ # 8 colours. For the standard GNU/Linux terminal emulator.
22
+ LINUX = [
23
+ [ 0, 0, 0], [128, 0, 0], [ 0, 128, 0],
24
+ [128, 128, 0], [ 0, 0, 128], [128, 0, 128],
25
+ [ 0, 128, 128], [192, 192, 192]
26
+ ]
27
+
28
+ # 16 colours. For cmd.exe on Windows and other miserable terminals.
29
+ SYSTEM = LINUX + [
30
+ [128, 128, 128], [255, 0, 0], [ 0, 255, 0], [255, 255, 0],
31
+ [ 0, 0, 255], [255, 0, 255], [ 0, 255, 255], [255, 255, 255]
11
32
  ]
12
33
 
34
+ # The next 216 colours. For men.
13
35
  COLORS = [
14
- [0, 0, 0], [0, 0, 95], [0, 0, 135], [0, 0, 175], [0, 0, 215], [0, 0, 255], [0, 95, 0], [0, 95, 95], [0, 95, 135], [0, 95, 175], [0, 95, 215], [0, 95, 255], [0, 135, 0], [0, 135, 95], [0, 135, 135], [0, 135, 175], [0, 135, 215], [0, 135, 255], [0, 175, 0], [0, 175, 95], [0, 175, 135], [0, 175, 175], [0, 175, 215], [0, 175, 255], [0, 215, 0], [0, 215, 95], [0, 215, 135], [0, 215, 175], [0, 215, 215], [0, 215, 255], [0, 255, 0], [0, 255, 95], [0, 255, 135], [0, 255, 175], [0, 255, 215], [0, 255, 255], [95, 0, 0], [95, 0, 95], [95, 0, 135], [95, 0, 175], [95, 0, 215], [95, 0, 255], [95, 95, 0], [95, 95, 95], [95, 95, 135], [95, 95, 175], [95, 95, 215], [95, 95, 255], [95, 135, 0], [95, 135, 95], [95, 135, 135], [95, 135, 175], [95, 135, 215], [95, 135, 255], [95, 175, 0], [95, 175, 95], [95, 175, 135], [95, 175, 175], [95, 175, 215], [95, 175, 255], [95, 215, 0], [95, 215, 95], [95, 215, 135], [95, 215, 175], [95, 215, 215], [95, 215, 255], [95, 255, 0], [95, 255, 95], [95, 255, 135], [95, 255, 175], [95, 255, 215], [95, 255, 255], [135, 0, 0], [135, 0, 95], [135, 0, 135], [135, 0, 175], [135, 0, 215], [135, 0, 255], [135, 95, 0], [135, 95, 95], [135, 95, 135], [135, 95, 175], [135, 95, 215], [135, 95, 255], [135, 135, 0], [135, 135, 95], [135, 135, 135], [135, 135, 175], [135, 135, 215], [135, 135, 255], [135, 175, 0], [135, 175, 95], [135, 175, 135], [135, 175, 175], [135, 175, 215], [135, 175, 255], [135, 215, 0], [135, 215, 95], [135, 215, 135], [135, 215, 175], [135, 215, 215], [135, 215, 255], [135, 255, 0], [135, 255, 95], [135, 255, 135], [135, 255, 175], [135, 255, 215], [135, 255, 255], [175, 0, 0], [175, 0, 95], [175, 0, 135], [175, 0, 175], [175, 0, 215], [175, 0, 255], [175, 95, 0], [175, 95, 95], [175, 95, 135], [175, 95, 175], [175, 95, 215], [175, 95, 255], [175, 135, 0], [175, 135, 95], [175, 135, 135], [175, 135, 175], [175, 135, 215], [175, 135, 255], [175, 175, 0], [175, 175, 95], [175, 175, 135], [175, 175, 175], [175, 175, 215], [175, 175, 255], [175, 215, 0], [175, 215, 95], [175, 215, 135], [175, 215, 175], [175, 215, 215], [175, 215, 255], [175, 255, 0], [175, 255, 95], [175, 255, 135], [175, 255, 175], [175, 255, 215], [175, 255, 255], [215, 0, 0], [215, 0, 95], [215, 0, 135], [215, 0, 175], [215, 0, 215], [215, 0, 255], [215, 95, 0], [215, 95, 95], [215, 95, 135], [215, 95, 175], [215, 95, 215], [215, 95, 255], [215, 135, 0], [215, 135, 95], [215, 135, 135], [215, 135, 175], [215, 135, 215], [215, 135, 255], [215, 175, 0], [215, 175, 95], [215, 175, 135], [215, 175, 175], [215, 175, 175], [215, 175, 215], [215, 175, 255], [215, 215, 0], [215, 215, 95], [215, 215, 135], [215, 215, 175], [215, 215, 215], [215, 215, 255], [215, 255, 0], [215, 255, 95], [215, 255, 135], [215, 255, 175], [215, 255, 215], [215, 255, 255], [255, 0, 0], [255, 0, 95], [255, 0, 135], [255, 0, 175], [255, 0, 215], [255, 0, 255], [255, 95, 0], [255, 95, 95], [255, 95, 135], [255, 95, 175], [255, 95, 215], [255, 95, 255], [255, 135, 0], [255, 135, 95], [255, 135, 135], [255, 135, 175], [255, 135, 215], [255, 135, 255], [255, 175, 0], [255, 175, 95], [255, 175, 135], [255, 175, 175], [255, 175, 215], [255, 175, 255], [255, 215, 0], [255, 215, 95], [255, 215, 135], [255, 215, 175], [255, 215, 215], [255, 215, 255], [255, 255, 0], [255, 255, 95], [255, 255, 135], [255, 255, 175], [255, 255, 215]
36
+ [ 0, 0, 0], [ 0, 0, 95], [ 0, 0, 135], [ 0, 0, 175],
37
+ [ 0, 0, 215], [ 0, 0, 255], [ 0, 95, 0], [ 0, 95, 95],
38
+ [ 0, 95, 135], [ 0, 95, 175], [ 0, 95, 215], [ 0, 95, 255],
39
+ [ 0, 135, 0], [ 0, 135, 95], [ 0, 135, 135], [ 0, 135, 175],
40
+ [ 0, 135, 215], [ 0, 135, 255], [ 0, 175, 0], [ 0, 175, 95],
41
+ [ 0, 175, 135], [ 0, 175, 175], [ 0, 175, 215], [ 0, 175, 255],
42
+ [ 0, 215, 0], [ 0, 215, 95], [ 0, 215, 135], [ 0, 215, 175],
43
+ [ 0, 215, 215], [ 0, 215, 255], [ 0, 255, 0], [ 0, 255, 95],
44
+ [ 0, 255, 135], [ 0, 255, 175], [ 0, 255, 215], [ 0, 255, 255],
45
+ [ 95, 0, 0], [ 95, 0, 95], [ 95, 0, 135], [ 95, 0, 175],
46
+ [ 95, 0, 215], [ 95, 0, 255], [ 95, 95, 0], [ 95, 95, 95],
47
+ [ 95, 95, 135], [ 95, 95, 175], [ 95, 95, 215], [ 95, 95, 255],
48
+ [ 95, 135, 0], [ 95, 135, 95], [ 95, 135, 135], [ 95, 135, 175],
49
+ [ 95, 135, 215], [ 95, 135, 255], [ 95, 175, 0], [ 95, 175, 95],
50
+ [ 95, 175, 135], [ 95, 175, 175], [ 95, 175, 215], [ 95, 175, 255],
51
+ [ 95, 215, 0], [ 95, 215, 95], [ 95, 215, 135], [ 95, 215, 175],
52
+ [ 95, 215, 215], [ 95, 215, 255], [ 95, 255, 0], [ 95, 255, 95],
53
+ [ 95, 255, 135], [ 95, 255, 175], [ 95, 255, 215], [ 95, 255, 255],
54
+ [135, 0, 0], [135, 0, 95], [135, 0, 135], [135, 0, 175],
55
+ [135, 0, 215], [135, 0, 255], [135, 95, 0], [135, 95, 95],
56
+ [135, 95, 135], [135, 95, 175], [135, 95, 215], [135, 95, 255],
57
+ [135, 135, 0], [135, 135, 95], [135, 135, 135], [135, 135, 175],
58
+ [135, 135, 215], [135, 135, 255], [135, 175, 0], [135, 175, 95],
59
+ [135, 175, 135], [135, 175, 175], [135, 175, 215], [135, 175, 255],
60
+ [135, 215, 0], [135, 215, 95], [135, 215, 135], [135, 215, 175],
61
+ [135, 215, 215], [135, 215, 255], [135, 255, 0], [135, 255, 95],
62
+ [135, 255, 135], [135, 255, 175], [135, 255, 215], [135, 255, 255],
63
+ [175, 0, 0], [175, 0, 95], [175, 0, 135], [175, 0, 175],
64
+ [175, 0, 215], [175, 0, 255], [175, 95, 0], [175, 95, 95],
65
+ [175, 95, 135], [175, 95, 175], [175, 95, 215], [175, 95, 255],
66
+ [175, 135, 0], [175, 135, 95], [175, 135, 135], [175, 135, 175],
67
+ [175, 135, 215], [175, 135, 255], [175, 175, 0], [175, 175, 95],
68
+ [175, 175, 135], [175, 175, 175], [175, 175, 215], [175, 175, 255],
69
+ [175, 215, 0], [175, 215, 95], [175, 215, 135], [175, 215, 175],
70
+ [175, 215, 215], [175, 215, 255], [175, 255, 0], [175, 255, 95],
71
+ [175, 255, 135], [175, 255, 175], [175, 255, 215], [175, 255, 255],
72
+ [215, 0, 0], [215, 0, 95], [215, 0, 135], [215, 0, 175],
73
+ [215, 0, 215], [215, 0, 255], [215, 95, 0], [215, 95, 95],
74
+ [215, 95, 135], [215, 95, 175], [215, 95, 215], [215, 95, 255],
75
+ [215, 135, 0], [215, 135, 95], [215, 135, 135], [215, 135, 175],
76
+ [215, 135, 215], [215, 135, 255], [215, 175, 0], [215, 175, 95],
77
+ [215, 175, 135], [215, 175, 175], [215, 175, 175], [215, 175, 215],
78
+ [215, 175, 255], [215, 215, 0], [215, 215, 95], [215, 215, 135],
79
+ [215, 215, 175], [215, 215, 215], [215, 215, 255], [215, 255, 0],
80
+ [215, 255, 95], [215, 255, 135], [215, 255, 175], [215, 255, 215],
81
+ [215, 255, 255], [255, 0, 0], [255, 0, 95], [255, 0, 135],
82
+ [255, 0, 175], [255, 0, 215], [255, 0, 255], [255, 95, 0],
83
+ [255, 95, 95], [255, 95, 135], [255, 95, 175], [255, 95, 215],
84
+ [255, 95, 255], [255, 135, 0], [255, 135, 95], [255, 135, 135],
85
+ [255, 135, 175], [255, 135, 215], [255, 135, 255], [255, 175, 0],
86
+ [255, 175, 95], [255, 175, 135], [255, 175, 175], [255, 175, 215],
87
+ [255, 175, 255], [255, 215, 0], [255, 215, 95], [255, 215, 135],
88
+ [255, 215, 175], [255, 215, 215], [255, 215, 255], [255, 255, 0],
89
+ [255, 255, 95], [255, 255, 135], [255, 255, 175], [255, 255, 215]
15
90
  ]
16
91
 
17
- GREYSCALE = (0x08..0xee).step(0x0a).map { |v| [v] * 3 }
92
+ # The next 16 colours. For zen.
93
+ GREYSCALE = (0x08..0xEE).step(0x0A).map { |v| [v] * 3 }
94
+
95
+ # Combine everything into a full featured 256 colour RGB model.
96
+ TABLE = SYSTEM + COLORS + GREYSCALE
97
+
98
+ # The key points that are used to calculate the nearest match of an RGB.
99
+ BYTEPOINTS_256 = [0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff]
100
+
101
+ # @param [Array<Integer>, String] value a String will be converted to Array
102
+ # @raise [TypeError] if the +value+ is neither Array or String
103
+ def initialize(value)
104
+ @value =
105
+ case value
106
+ when Array
107
+ validate_array(value)
108
+ value
109
+ when String
110
+ validate_array(value = value.scan(/\d+/).map!(&:to_i))
111
+ value
112
+ else
113
+ raise TypeError, "can't convert #{ value.class } into PryTheme::RGB"
114
+ end
115
+ end
116
+
117
+ # @return [String]
118
+ def inspect
119
+ "(RGB: #{ to_s })"
120
+ end
121
+
122
+ # Converts the RGB to a terminal colour equivalent.
123
+ #
124
+ # @note Accepts the following numbers: 256, 16, 8.
125
+ # @param [Integer] color_model
126
+ # @raise [ArgumentError] if +color_model+ parameter is incorrect
127
+ # @return [TERM] a TERM representation of the RGB
128
+ def to_term(color_model = 256)
129
+ term = case color_model
130
+ when 256 then PryTheme::RGB::TABLE.index(@value)
131
+ when 16 then PryTheme::RGB::SYSTEM.index(@value)
132
+ when 8 then PryTheme::RGB::LINUX.index(@value)
133
+ else raise ArgumentError,
134
+ "invalid value for PryTheme::HEX#to_term(): #{ @value }"
135
+ end
136
+ term = find_among_term_colors(term, color_model) if term.nil?
137
+ PryTheme::TERM.new(term, color_model)
138
+ end
139
+
140
+ # Converts the RGB to a HEX colour equivalent.
141
+ # @return [HEX] a HEX representation of the RGB
142
+ def to_hex
143
+ PryTheme::HEX.new("#%02x%02x%02x" % @value)
144
+ end
145
+
146
+ # @example
147
+ # RGB.new([0, 12, 255]).to_s #=> "0, 12, 255"
148
+ # @return [String]
149
+ def to_s
150
+ @value.join(', ')
151
+ end
152
+
153
+ # @example
154
+ # RGB.new([0, 12, 255]).to_s #=> [0, 12, 255]
155
+ # @return [Array<Integer>]
156
+ def to_a
157
+ @value
158
+ end
159
+
160
+ # @example
161
+ # RGB.new([0, 0, 0]).to_css #=> 'rgb(0, 0, 0)'
162
+ # @return [String]
163
+ def to_css
164
+ "rgb(#{ to_s })"
165
+ end
166
+
167
+ private
168
+
169
+ # Checks whether the +ary+ has correct number of elements and these elements
170
+ # are valid RGB numbers.
171
+ #
172
+ # @param [Array<Integer>] ary
173
+ # @raise [ArgumentError] if the +ary+ is invalid
174
+ # @return [void]
175
+ def validate_array(ary)
176
+ correct_size = ary.size.equal?(3)
177
+ correct_vals = ary.all?{ |val| val.is_a?(Fixnum) && val.between?(0, 255) }
178
+ return true if correct_size && correct_vals
179
+ raise ArgumentError,
180
+ %|invalid value for PryTheme::RGB#validate_array(): "#{ ary }"|
181
+ end
182
+
183
+ # Approximates the given +byte+ to a terminal colour value within range of
184
+ # 256 colours.
185
+ #
186
+ # @param [Integer] byte a number between 0 and 255
187
+ # @return [Integer] approximated number
188
+ def nearest_term_256(byte)
189
+ for i in 0..4
190
+ lower, upper = BYTEPOINTS_256[i], BYTEPOINTS_256[i + 1]
191
+ next unless byte.between?(lower, upper)
192
+
193
+ distance_from_lower = (lower - byte).abs
194
+ distance_from_upper = (upper - byte).abs
195
+ closest = distance_from_lower < distance_from_upper ? lower : upper
196
+ end
197
+ closest
198
+ end
199
+
200
+ # The same as {#nearest_term_256}, but returns a number beteen 0 and 15.
201
+ #
202
+ # @note Oh, come on. At least it works!
203
+ # @todo use more realistic algorithm.
204
+ def nearest_term_16(byte)
205
+ byte / 16
206
+ end
207
+
208
+ # The same as {#nearest_term_256}, but returns a number beteen 0 and 7.
209
+ #
210
+ # @note Oh, come on. At least it works!
211
+ # @todo use more realistic algorithm.
212
+ def nearest_term_8(byte)
213
+ byte / 32
214
+ end
215
+
216
+ # Finds an approximated +term+ colour among the colour numbers within the
217
+ # given +color_model+.
218
+ #
219
+ # @param [Integer] term a colour to be approximated
220
+ # @param [Integer] color_model possible values {#to_term}
221
+ # @return [Integer] approximated number, which fits in range of color_model
222
+ def find_among_term_colors(term, color_model)
223
+ rgb = @value.map { |byte| nearest_term_256(byte) }
224
+ term = PryTheme::RGB::TABLE.index(rgb)
225
+ approximate(term, color_model)
226
+ end
227
+
228
+ # Approximates +term+ in correspondence with +color_model+
229
+ #
230
+ # @see #nearest_term_16
231
+ # @see #nearest_term_8
232
+ # @param [Integer] term a colour to be approximated
233
+ # @param [Integer] color_model possible values {#to_term}
234
+ # @return [Integer] approximated number, which fits in range of color_model
235
+ def approximate(term, color_model)
236
+ needs_approximation = (term > color_model - 1)
18
237
 
19
- def self.table
20
- SYSTEM + COLORS + GREYSCALE
238
+ if needs_approximation
239
+ case color_model
240
+ when 16 then nearest_term_16(term)
241
+ when 8 then nearest_term_8(term)
242
+ end
243
+ else
244
+ term
245
+ end
21
246
  end
22
247
 
23
248
  end