"lib/puppet/dotfiles/lib/puppet/dotfiles/lib" did not exist on "1042df79ab6e547769d2f35b7f58142943690f3c"
colorscheme.rb 5.09 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
#
# colorscheme.rb
#
# My own take on the [Base16](https://github.com/chriskempson/base16)
# builders and templates, primarily so I can integrate it into my
# ERB-based dotfiles, managed by Puppet at:
#
# https://github.com/iamjamestl/dotfiles/blob/master/bin/refresh-dotfiles
#
# This has also been carefully extended to work well with low-color
# terminals, like the Linux console.
#

14
15
16
17
18
19
require 'yaml'

class ColorScheme
  class Color
    attr_reader :hex, :hexr, :hexg, :hexb
    attr_reader :r, :g, :b
20
    attr_reader :base, :ansi, :bold
21

22
    def initialize(colorscheme, hex, base, ansi, bold)
23
24
25
26
      @colorscheme = colorscheme
      @hex = hex
      (@hexr, @hexg, @hexb) = hex.scan(/../)
      (@r, @g, @b) = hex.scan(/../).map { |color| color.to_i(16) }
27
28
29
      @base = base
      @ansi = ansi
      @bold = bold
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    end

    # Modified from the original base16 builder
    # See: https://github.com/chriskempson/base16-builder/blob/dfe4cbbf7941ee13ddd9890e74e8787e16375dff/base16#L176
    def mix(color, weight = 50)
      p = (weight / 100.0).to_f
      w = p * 2 - 1

      w1 = (w + 1) / 2.0
      w2 = 1 - w1


      Color.new(@colorscheme, "%02x%02x%02x" % [
        (color.r * w1 + r * w2).to_i,
James T. Lee's avatar
James T. Lee committed
44
45
        (color.g * w1 + g * w2).to_i,
        (color.b * w1 + b * w2).to_i,
46
      ], nil, nil, nil)
47
48
49
50
51
    end

    # Add a bit of 'white' to a color.  White is defined by color 7, the end of the
    # gray scale in a color scheme.  (This is usually #FFFFFF, or close.)
    def tint(weight = 50)
52
      mix(@colorscheme.colors_by_base[0x07], weight)
53
54
    end

55
    # Add a bit of 'black' to a color.  Black is defined by color 0, the end of the
56
    # gray scale in a color scheme.  (This is usually #000000, or close.)
57
    def shade(weight = 50)
58
      mix(@colorscheme.colors_by_base[0x00], weight)
59
60
    end

61
62
63
64
    def invert
      Color.new(@colorscheme, "%02x%02x%02x" % rgb.map { |n| n ^ 255 }, nil, nil, nil)
    end

65
    def x11
66
      "rgb:#{hexr}/#{hexg}/#{hexb}"
67
    end
68
69
70
71

    def rgb
      [r, g, b]
    end
James T. Lee's avatar
James T. Lee committed
72
73
74
75

    def web
      "##{hexr}#{hexg}#{hexb}"
    end
76
77
  end

78
79
80
81
  # Map the base16 code to ansi number based on the number of available
  # colors.  Where approximations had to be made, they were done subjectively
  # by look (https://chriskempson.github.io/base16/) and style guidelines
  # (https://github.com/chriskempson/base16/blob/master/styling.md).
82
83
84
85
86
87
88
  #
  # Base0F is the hard one.  It's rarely used, has no historical mapping to
  # any particular color, and varies wildly across different Base16 themes.
  # This mapping approximates it to Base03 for low color terminals, which is
  # usually a middle gray of similar luminance.  I've never seen it used for
  # a background color, but if it is, it will take on the middle gray color
  # on 16-color terminals, and the default background color on 8-color ones.
James T. Lee's avatar
James T. Lee committed
89
  BASE_TO_ANSI = {
90
91
92
93
         # 00  01  02 03  04 05  06  07    08  09     0A     0B     0C     0D    0E   0F
    256 => [0, 18, 19, 8, 20, 7, 21, 15,[1,9], 16,[3,11],[2,10],[6,14],[4,12],[5,13], 17],
    16  => [0,  0,  0, 8,  7, 7, 15, 15,[1,9],  3,[3,11],[2,10],[6,14],[4,12],[5,13],  8],
    8   => [0,  0,  0, 8,  7, 7, 15, 15,    1,  3,     3,     2,     6,     4,     5,  8],
94
95
  }

96
97
98
99
100
101
102
103
  def self.dark
    ColorScheme.new('.colorscheme-dark.yaml')
  end

  def self.light
    ColorScheme.new('.colorscheme-light.yaml')
  end

James T. Lee's avatar
James T. Lee committed
104
105
106
107
  def self.bright
    ColorScheme.new('.colorscheme-bright.yaml')
  end

108
  def initialize(filename)
109
    @scheme_data = YAML.load_file(File.join(File.dirname(__FILE__), '..', '..', '..', '..', filename))
110
    @colors = BASE_TO_ANSI.keys.each_with_object(Hash.new) do |term_colors, colors_acc|
111
      colors_acc[term_colors] = BASE_TO_ANSI[term_colors].each_with_index.map do |ansis, base|
112
113
        basestr = '%02X' % base
        hex = @scheme_data["base#{basestr}"]
114
        Array(ansis).map do |ansi|
115
116
117
118
119
120
121
          # Map requested ansi color into the available color space.
          # If a mapping is made, pass a flag that bold styling could be used
          # to "jump" back to the original color (for example, on the Linux
          # console, to be able to access all 16 colors from the 8 color space).
          bold = ansi / term_colors > 0
          ansi %= term_colors
          Color.new(self, hex, basestr, ansi, bold)
122
123
        end
      end.flatten
124
      colors_acc
125
126
127
    end
  end

128
129
  def colors(term_colors = 256)
    @colors[term_colors]
130
131
  end

132
  def colors_by_ansi(term_colors = 256)
133
    colors(term_colors).sort do |c1, c2|
134
      if c1.ansi == c2.ansi
135
        c1.base <=> c2.base
136
137
138
      else
        c1.ansi <=> c2.ansi
      end
139
    end.uniq { |color| color.ansi }
140
141
  end

142
  def colors_by_base(term_colors = 256)
143
    colors(term_colors).sort do |c1, c2|
144
145
146
      if c1.base == c2.base
        c1.ansi <=> c2.ansi
      else
147
        c1.base <=> c2.base
148
149
      end
    end.uniq { |color| color.base }
150
151
  end

James T. Lee's avatar
James T. Lee committed
152
  def terminal
153
    colors_by_ansi(256)
154
155
  end

James T. Lee's avatar
James T. Lee committed
156
  def console
James T. Lee's avatar
James T. Lee committed
157
    colors_by_ansi(256).take(16)
James T. Lee's avatar
James T. Lee committed
158
159
160
  end

  def foreground
161
    colors_by_base[0x05]
162
  end
163

James T. Lee's avatar
James T. Lee committed
164
  def background
165
    colors_by_base[0x00]
166
  end
167

James T. Lee's avatar
James T. Lee committed
168
  def cursor
169
    colors_by_base[0x05]
170
  end
James T. Lee's avatar
James T. Lee committed
171
172
173
174

  def bold_colors(term_colors = 256)
    colors_by_base(term_colors).keep_if { |color| color.bold }
  end
175
end