pry-theme 0.1.3 → 0.2.0

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 (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