paintbrush 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7c87f82d4c60332bb80901c9b53ba99d027190044b8b6a8a0d91f0577c870846
4
- data.tar.gz: cb1e9bd1e7c4fc0b65ac0ae6e50ce02dd62e86d7845192b1b14d18a602f069ce
3
+ metadata.gz: cb568b4656208e4b1d5e134b9a6982a50be63f9da4a776aa7210a302f65a59ce
4
+ data.tar.gz: cc92c16814e12eb90e089c5affaa6c4739ecbec1f9121093739c6e776e2aaf0f
5
5
  SHA512:
6
- metadata.gz: 21bacc0759c545c65c66d70e0fbdb7b406ea1fa224ebcab36e02d9f82ba4e4c0bdbb33dc34f61f52224272ee8c49cae3641d7ca48fdaf0bd5278ae3a3fdf6b08
7
- data.tar.gz: a1340e727fe19c354cb5ae43202afe2ddfbdb94d8748cb8cba16edac3c1d88f3293e6825156c65043329d81f84a78ae041adc6e7d99fad52d64fe8cfe5513ea6
6
+ metadata.gz: dbc3a7c11aedf89c5191e1a538b902235c84e7a540b7d155ee008c1ad58e56a2eb4f139f151d681f06ba93fc7345221130ba0972d7d331ac6e793ef8b7752172
7
+ data.tar.gz: c2bc6bb3a2e09ec5c9aa2d25c47f1b118cf01e2d10eef340e430806531c05421b11e7292d677d96f12112f1d252d64cb1c673a1f50bcf99d6ac8c0a493a98116
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- paintbrush (0.1.0)
4
+ paintbrush (0.1.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
data/Rakefile CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bundler/gem_tasks'
4
4
  require 'rspec/core/rake_task'
5
+ require 'paintbrush'
5
6
 
6
7
  RSpec::Core::RakeTask.new(:spec)
7
8
 
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paintbrush
4
+ # Wraps a Paintbrush::ColorElement instance and maps its start and end boundaries within a
5
+ # compiled escaped string by matching specific unique (indexed) escape codes. Provides
6
+ # `#surround?` for detecting if another element exists within the current element's boundaries.
7
+ class BoundedColorElement
8
+ def initialize(color_element:, escaped_output:)
9
+ @color_element = color_element
10
+ @escaped_output = escaped_output
11
+ end
12
+
13
+ def surround?(element)
14
+ return false if element == self
15
+ return false unless element.open_index.between?(open_index, close_index)
16
+ return false unless element.close_index.between?(open_index, close_index)
17
+
18
+ true
19
+ end
20
+
21
+ def inspect
22
+ "<#{self.class} boundaries=#{boundaries}>"
23
+ end
24
+
25
+ def index
26
+ color_element.index
27
+ end
28
+
29
+ def code
30
+ color_element.code
31
+ end
32
+
33
+ def boundaries
34
+ @boundaries ||= [open_index, close_index]
35
+ end
36
+
37
+ def open_index
38
+ @open_index ||= escaped_output.index(Escapes.open(index))
39
+ end
40
+
41
+ def close_index
42
+ escaped_output.index(Escapes.close(index))
43
+ end
44
+
45
+ private
46
+
47
+ attr_reader :escaped_output, :color_element
48
+ end
49
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paintbrush
4
+ # Provides a substring enclosed in unique escape codes for later colorization when the full
5
+ # string has been created and all interpolation is completed. Adds itself to a provided stack
6
+ # of ColorElement objects on initialization.
7
+ class ColorElement
8
+ attr_reader :stack, :code, :string, :index
9
+ attr_accessor :open_index, :close_index
10
+
11
+ def initialize(stack:, code:, string:)
12
+ @stack = stack
13
+ @code = code
14
+ @string = string
15
+ @index = stack.size
16
+ stack << self
17
+ end
18
+
19
+ def to_s
20
+ "#{Escapes.open(index)}#{string}#{Escapes.close(index)}"
21
+ end
22
+
23
+ def inspect
24
+ "<#{self.class.name} index=#{index} code=#{code}>"
25
+ end
26
+ end
27
+ end
@@ -7,7 +7,7 @@ module Paintbrush
7
7
  class ColorizedString
8
8
  def initialize(&block)
9
9
  @block = block
10
- @codes = []
10
+ @stack = []
11
11
  end
12
12
 
13
13
  # Returns a colorized string by injecting escape codes into the various calls to each color
@@ -19,30 +19,43 @@ module Paintbrush
19
19
 
20
20
  private
21
21
 
22
- attr_reader :block, :codes
22
+ attr_reader :block, :stack
23
23
 
24
- def colorized_string
25
- codes.each.with_index.reduce(escaped_output) do |string, (code, index)|
26
- restored_color_code = index + 1 == codes.size ? '0' : codes[index + 1]
27
- subbed_string(string, index, code, restored_color_code)
24
+ def colorized_string(string: escaped_output, tree: element_tree)
25
+ tree[:children].reduce(string) do |output, child|
26
+ subbed = subbed_string(output, child[:node], tree[:node])
27
+ next subbed if child[:children].empty?
28
+
29
+ colorized_string(string: subbed, tree: child)
30
+ end
31
+ end
32
+
33
+ def bounded_color_elements
34
+ @bounded_color_elements ||= stack.map do |color_element|
35
+ BoundedColorElement.new(color_element: color_element, escaped_output: escaped_output)
28
36
  end
29
37
  end
30
38
 
31
- def subbed_string(string, index, code, restored_color_code)
39
+ def element_tree
40
+ @element_tree ||= ElementTree.new(bounded_color_elements: bounded_color_elements).tree
41
+ end
42
+
43
+ def subbed_string(string, color_element, parent_color_element)
44
+ restored_color_code = parent_color_element.nil? ? '0' : parent_color_element.code
32
45
  string
33
- .sub("#{Colors::ESCAPE_START_OPEN}#{index}#{Colors::ESCAPE_START_CLOSE}", "\e[#{code}m")
34
- .sub("#{Colors::ESCAPE_END_OPEN}#{index}#{Colors::ESCAPE_END_CLOSE}", "\e[0m\e[#{restored_color_code}m")
46
+ .sub(Escapes.open(color_element.index).to_s, "\e[#{color_element.code}m")
47
+ .sub(Escapes.close(color_element.index).to_s, "\e[0m\e[#{restored_color_code}m")
35
48
  end
36
49
 
37
50
  def escaped_output
38
- context.instance_eval(&block)
51
+ @escaped_output ||= context.instance_eval(&block)
39
52
  end
40
53
 
41
54
  def context
42
55
  eval('self', block.binding, __FILE__, __LINE__).dup.tap do |context|
43
56
  context.send(:include, Paintbrush::Colors) if context.respond_to?(:include)
44
57
  context.send(:extend, Paintbrush::Colors) if context.respond_to?(:extend)
45
- context.send(:instance_variable_set, :@__codes, codes)
58
+ context.send(:instance_variable_set, :@__stack, stack)
46
59
  end
47
60
  end
48
61
  end
@@ -6,11 +6,6 @@ module Paintbrush
6
6
  # string to be reconstituted afterwards with nested strings restoring the previous color once
7
7
  # they have terminated.
8
8
  module Colors
9
- ESCAPE_START_OPEN = "\e[3;15;17]OPEN:"
10
- ESCAPE_START_CLOSE = "\e[3;15;17]CLOSE:"
11
- ESCAPE_END_OPEN = "\e[17;15;3]OPEN:"
12
- ESCAPE_END_CLOSE = "\e[17;15;3]CLOSE:"
13
-
14
9
  COLOR_CODES = {
15
10
  black: '30',
16
11
  red: '31',
@@ -25,10 +20,7 @@ module Paintbrush
25
20
 
26
21
  COLOR_CODES.each do |name, code|
27
22
  define_method name do |string|
28
- @__codes.push(code)
29
- "#{ESCAPE_START_OPEN}#{@__codes.size - 1}#{ESCAPE_START_CLOSE}" \
30
- "#{string}" \
31
- "#{ESCAPE_END_OPEN}#{@__codes.size - 1}#{ESCAPE_END_CLOSE}"
23
+ ColorElement.new(stack: @__stack, code: code, string: string).to_s
32
24
  end
33
25
  end
34
26
  end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paintbrush
4
+ # A tree of BoundedColorElement objects. Used to build a full tree of colorized substrings in
5
+ # order to allow discovery of parent substrings and use their color code to restore to when the
6
+ # substring is terminated. Allows deeply-nested colorized strings.
7
+ class ElementTree
8
+ attr_reader :tree
9
+
10
+ def initialize(bounded_color_elements:)
11
+ @bounded_color_elements = bounded_color_elements
12
+ @tree = { node: nil, children: [] }
13
+ build_tree(boundary_end: bounded_color_elements.map(&:close_index).max)
14
+ end
15
+
16
+ def build_tree(boundary_end:, root: @tree, elements: bounded_color_elements, boundary_start: 0)
17
+ root_nodes, non_root_nodes = partitioned_elements(elements, boundary_start, boundary_end)
18
+
19
+ root_nodes.each do |node|
20
+ root[:children] << child_node(node)
21
+ build_tree(
22
+ root: root[:children].last,
23
+ elements: non_root_nodes,
24
+ boundary_start: node.open_index,
25
+ boundary_end: node.close_index
26
+ )
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ attr_reader :bounded_color_elements
33
+
34
+ def child_node(node)
35
+ { node: node, children: [] }
36
+ end
37
+
38
+ def partitioned_elements(elements, boundary_start, boundary_end)
39
+ elements.partition do |element|
40
+ next false if elements.any? { |child| child.surround?(element) }
41
+
42
+ element.boundaries.all? { |index| index.between?(boundary_start, boundary_end) }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Paintbrush
4
+ # Provides an authority on escape code generation. Provides `.close` and `.open`, both of which
5
+ # receive an index (i.e. the current size of the stack). Used for escape code insertion and comparison.
6
+ module Escapes
7
+ def self.open(index)
8
+ "\e[3;15;17]START_OPEN:#{index}:\e[3;15;17]START_CLOSE"
9
+ end
10
+
11
+ def self.close(index)
12
+ "\e[17;15;3]END_OPEN:#{index}:\e[17;15;3]END_CLOSE"
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Paintbrush
4
- VERSION = '0.1.0'
4
+ VERSION = '0.1.1'
5
5
  end
data/lib/paintbrush.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'paintbrush/version'
4
+ require_relative 'paintbrush/escapes'
4
5
  require_relative 'paintbrush/colors'
5
6
  require_relative 'paintbrush/colorized_string'
7
+ require_relative 'paintbrush/color_element'
8
+ require_relative 'paintbrush/bounded_color_element'
9
+ require_relative 'paintbrush/element_tree'
6
10
 
7
11
  # Colorizes a string, provides `#paintbrush`. When included/extended in a class, call
8
12
  # `#paintbrush` and pass a block to use the provided dynamically defined methods, e.g.:
@@ -16,9 +20,11 @@ require_relative 'paintbrush/colorized_string'
16
20
  # end
17
21
  # ```
18
22
  module Paintbrush
19
- class Error < StandardError; end
23
+ def self.paintbrush(&block)
24
+ ColorizedString.new(&block).colorized
25
+ end
20
26
 
21
27
  def paintbrush(&block)
22
- ColorizedString.new(&block).colorized
28
+ Paintbrush.paintbrush(&block)
23
29
  end
24
30
  end
@@ -0,0 +1,312 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet"
4
+ href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css"
5
+ integrity="sha512-SbiR/eusphKoMVVXysTKG/7VseWii+Y3FdHrt0EpKgpToZeemhqHeZeLWLhJutz/2ut2Vw1uQEj2MbRF+TVBUA=="
6
+ crossorigin="anonymous"
7
+ referrerpolicy="no-referrer" />
8
+ <style>
9
+
10
+ .code {
11
+ font-family: monospace;
12
+ max-height: 30rem;
13
+ overflow-y: auto;
14
+ }
15
+
16
+ h1.title {
17
+ display: inline;
18
+ }
19
+
20
+ .header .separator {
21
+ display: inline-block;
22
+ height: 2.5rem;
23
+ }
24
+
25
+ .version {
26
+ color: #bbb;
27
+ font-size: 2rem;
28
+ }
29
+
30
+ .ansi-html {
31
+ display: inline-block;
32
+ background-color: #2e2e2e;
33
+ border: 5px solid #2e2e2e;
34
+ border-radius: 10px;
35
+ }
36
+
37
+ .ansi-html .ansi-color-0 { color: #555555; }
38
+ .ansi-html .ansi-color-1 { color: #ff0000; }
39
+ .ansi-html .ansi-color-2 { color: #47ff47; }
40
+ .ansi-html .ansi-color-3 { color: #e9e947; }
41
+ .ansi-html .ansi-color-4 { color: #49a0dd; }
42
+ .ansi-html .ansi-color-5 { color: #8d7eeb; }
43
+ .ansi-html .ansi-color-6 { color: #2eecff; }
44
+ .ansi-html .ansi-color-7 { color: #ffffff; }
45
+ .ansi-html .ansi-color-9 { color: #606060; }
46
+ .ansi-html .ansi-color-reset { color: #dddddd; }
47
+
48
+ .highlight table td { padding: 5px; }
49
+ .highlight table pre { margin: 0; }
50
+ .highlight, .highlight .w {
51
+ color: #24292f;
52
+ background-color: #f6f8fa;
53
+ }
54
+ .highlight .k, .highlight .kd, .highlight .kn, .highlight .kp, .highlight .kr, .highlight .kt, .highlight .kv {
55
+ color: #cf222e;
56
+ }
57
+ .highlight .gr {
58
+ color: #f6f8fa;
59
+ }
60
+ .highlight .gd {
61
+ color: #82071e;
62
+ background-color: #ffebe9;
63
+ }
64
+ .highlight .nb {
65
+ color: #953800;
66
+ }
67
+ .highlight .nc {
68
+ color: #953800;
69
+ }
70
+ .highlight .no {
71
+ color: #953800;
72
+ }
73
+ .highlight .nn {
74
+ color: #953800;
75
+ }
76
+ .highlight .sr {
77
+ color: #116329;
78
+ }
79
+ .highlight .na {
80
+ color: #116329;
81
+ }
82
+ .highlight .nt {
83
+ color: #116329;
84
+ }
85
+ .highlight .gi {
86
+ color: #116329;
87
+ background-color: #dafbe1;
88
+ }
89
+ .highlight .kc {
90
+ color: #0550ae;
91
+ }
92
+ .highlight .l, .highlight .ld, .highlight .m, .highlight .mb, .highlight .mf, .highlight .mh, .highlight .mi, .highlight .il, .highlight .mo, .highlight .mx {
93
+ color: #0550ae;
94
+ }
95
+ .highlight .sb {
96
+ color: #0550ae;
97
+ }
98
+ .highlight .bp {
99
+ color: #0550ae;
100
+ }
101
+ .highlight .ne {
102
+ color: #0550ae;
103
+ }
104
+ .highlight .nl {
105
+ color: #0550ae;
106
+ }
107
+ .highlight .py {
108
+ color: #0550ae;
109
+ }
110
+ .highlight .nv, .highlight .vc, .highlight .vg, .highlight .vi, .highlight .vm {
111
+ color: #0550ae;
112
+ }
113
+ .highlight .o, .highlight .ow {
114
+ color: #0550ae;
115
+ }
116
+ .highlight .gh {
117
+ color: #0550ae;
118
+ font-weight: bold;
119
+ }
120
+ .highlight .gu {
121
+ color: #0550ae;
122
+ font-weight: bold;
123
+ }
124
+ .highlight .s, .highlight .sa, .highlight .sc, .highlight .dl, .highlight .sd, .highlight .s2, .highlight .se, .highlight .sh, .highlight .sx, .highlight .s1, .highlight .ss {
125
+ color: #0a3069;
126
+ }
127
+ .highlight .nd {
128
+ color: #8250df;
129
+ }
130
+ .highlight .nf, .highlight .fm {
131
+ color: #8250df;
132
+ }
133
+ .highlight .err {
134
+ color: #f6f8fa;
135
+ background-color: #82071e;
136
+ }
137
+ .highlight .c, .highlight .ch, .highlight .cd, .highlight .cm, .highlight .cp, .highlight .cpf, .highlight .c1, .highlight .cs {
138
+ color: #6e7781;
139
+ }
140
+ .highlight .gl {
141
+ color: #6e7781;
142
+ }
143
+ .highlight .gt {
144
+ color: #6e7781;
145
+ }
146
+ .highlight .ni {
147
+ color: #24292f;
148
+ }
149
+ .highlight .si {
150
+ color: #24292f;
151
+ }
152
+ .highlight .ge {
153
+ color: #24292f;
154
+ font-style: italic;
155
+ }
156
+ .highlight .gs {
157
+ color: #24292f;
158
+ font-weight: bold;
159
+ }
160
+
161
+ </style>
162
+ </head>
163
+
164
+ <body>
165
+ <div class="container m-3 p-3">
166
+ <div class="header p-3 m-3 w-90 text-end">
167
+ <h1 class="title">paintbrush</h1>
168
+ <span class="separator border-end pb-2 ms-2 me-2"></span>
169
+ <span class="version">0.1.0</span>
170
+ </div>
171
+
172
+ <hr/>
173
+
174
+
175
+ <div class="row">
176
+ <div class="col-auto page-tree fs-6 mt-1 ms-3 me-3">
177
+
178
+ <li>
179
+
180
+ <a href='/home/bob/dev/paintbrush/rspec-documentation/bundle/introduction.html'>Introduction</a>
181
+
182
+ </li>
183
+
184
+ <ul>
185
+
186
+ </ul>
187
+
188
+ </div>
189
+
190
+ <div class="col">
191
+ <h1 id="paintbrush">Paintbrush</h1>
192
+
193
+ <p>Simple and concise string colorization for <em>Ruby</em> without overloading <code>String</code> methods or requiring verbose class/method invocation.</p>
194
+
195
+ <p><em>Paintbrush</em> has zero dependencies and does not pollute any namespaces or objects outside of the <code>#paintbrush</code> method wherever you include the <code>Paintbrush</code> module.</p>
196
+
197
+ <p>Nesting is supported, allowing you to use multiple colors within the same string. The previous color is automatically restored.</p>
198
+
199
+ <div location="9"><button type="button" class="btn btn-primary float-end" data-bs-toggle="modal" data-bs-target="#modal-15cae418-087d-45aa-b936-ea2c34033c01-side-by-side"> View Side-by-side </button> <div class="modal fade" id="modal-15cae418-087d-45aa-b936-ea2c34033c01-side-by-side" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
200
+ <div class="modal-dialog modal-xl">
201
+ <div class="modal-content">
202
+ <div class="modal-header">
203
+ <h5 class="modal-title" id="modal-15cae418-087d-45aa-b936-ea2c34033c01-side-by-side-label">Side-by-side view</h5>
204
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
205
+ </div>
206
+ <div class="modal-body">
207
+ <div class="container p-3">
208
+ <div class="row border">
209
+ <div class="col border">
210
+ <div class="p-3 mb-5 code highlight">
211
+ <span class="nb">require</span> <span class="s1">'paintbrush'</span><br /><br /><span class="kp">extend</span> <span class="no">Paintbrush</span><br /><br /><span class="n">output</span> <span class="o">=</span> <span class="n">paintbrush</span> <span class="p">{</span> <span class="n">purple</span> <span class="s2">"You used </span><span class="si">#{</span><span class="n">green</span> <span class="s1">'four'</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">blue</span> <span class="s2">"(</span><span class="si">#{</span><span class="n">cyan</span> <span class="s1">'4'</span><span class="si">}</span><span class="s2">)"</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">yellow</span> <span class="s1">'colors'</span><span class="si">}</span><span class="s2"> today!"</span> <span class="p">}</span><br /><span class="n">it_documents</span> <span class="n">output</span> <span class="k">do</span><br />  <span class="n">expect</span><span class="p">(</span><span class="n">output</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span> <span class="s2">"</span><span class="se">\e</span><span class="s2">[35mYou used </span><span class="se">\e</span><span class="s2">[32mfour</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m "</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[34m(</span><span class="se">\e</span><span class="s2">[36m4</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[34m)</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m "</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[33mcolors</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m today!</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m"</span><br /><span class="k">end</span><br />
212
+ </div>
213
+ </div>
214
+ <div class="col border">
215
+ <div class="p-3 mb-5 code highlight">
216
+ <div class="ansi-html border m-1 p-4">
217
+ <span></span><span class="ansi-color-5">You used </span><span class="ansi-color-2">four</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> </span><span class="ansi-color-4">(</span><span class="ansi-color-6">4</span><span class="ansi-color-reset"></span><span class="ansi-color-4">)</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> </span><span class="ansi-color-3">colors</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> today!</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"></span>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ </div>
222
+ </div>
223
+ </div>
224
+ <div class="modal-footer">
225
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ <ul class="nav nav-tabs" id="html-tabs-15cae418-087d-45aa-b936-ea2c34033c01" role="tablist">
231
+ <li class="nav-item" role="presentation"><button class="nav-link active" id="code-source-15cae418-087d-45aa-b936-ea2c34033c01-tab" data-bs-toggle="tab" data-bs-target="#code-source-15cae418-087d-45aa-b936-ea2c34033c01" type="button" role="tab" aria-controls="code-source" aria-selected="true">Code</button></li>
232
+ <li class="nav-item" role="presentation"><button class="nav-link" id="rendered-15cae418-087d-45aa-b936-ea2c34033c01-tab" data-bs-toggle="tab" data-bs-target="#rendered-15cae418-087d-45aa-b936-ea2c34033c01" type="button" role="tab" aria-controls="rendered" aria-selected="false">Output</button></li>
233
+ </ul>
234
+ <div class="tab-content" id="html-tab-content-15cae418-087d-45aa-b936-ea2c34033c01">
235
+ <div class="tab-pane fade show active" id="code-source-15cae418-087d-45aa-b936-ea2c34033c01" role="tabpanel" aria-labelledby="code-source-15cae418-087d-45aa-b936-ea2c34033c01-tab">
236
+ <div class="p-3 mb-5 code highlight">
237
+ <span class="nb">require</span> <span class="s1">'paintbrush'</span><br /><br /><span class="kp">extend</span> <span class="no">Paintbrush</span><br /><br /><span class="n">output</span> <span class="o">=</span> <span class="n">paintbrush</span> <span class="p">{</span> <span class="n">purple</span> <span class="s2">"You used </span><span class="si">#{</span><span class="n">green</span> <span class="s1">'four'</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">blue</span> <span class="s2">"(</span><span class="si">#{</span><span class="n">cyan</span> <span class="s1">'4'</span><span class="si">}</span><span class="s2">)"</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">yellow</span> <span class="s1">'colors'</span><span class="si">}</span><span class="s2"> today!"</span> <span class="p">}</span><br /><span class="n">it_documents</span> <span class="n">output</span> <span class="k">do</span><br />  <span class="n">expect</span><span class="p">(</span><span class="n">output</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span> <span class="s2">"</span><span class="se">\e</span><span class="s2">[35mYou used </span><span class="se">\e</span><span class="s2">[32mfour</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m "</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[34m(</span><span class="se">\e</span><span class="s2">[36m4</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[34m)</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m "</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[33mcolors</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[35m today!</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m"</span><br /><span class="k">end</span><br />
238
+ </div>
239
+ </div>
240
+ <div class="tab-pane fade" id="rendered-15cae418-087d-45aa-b936-ea2c34033c01" role="tabpanel" aria-labelledby="rendered-15cae418-087d-45aa-b936-ea2c34033c01-tab">
241
+ <div class="p-3 mb-5 code highlight">
242
+ <div class="ansi-html border m-1 p-4">
243
+ <span></span><span class="ansi-color-5">You used </span><span class="ansi-color-2">four</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> </span><span class="ansi-color-4">(</span><span class="ansi-color-6">4</span><span class="ansi-color-reset"></span><span class="ansi-color-4">)</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> </span><span class="ansi-color-3">colors</span><span class="ansi-color-reset"></span><span class="ansi-color-5"> today!</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"></span>
244
+ </div>
245
+ </div>
246
+ </div>
247
+ </div>
248
+ </div>
249
+
250
+ <div location="22"><button type="button" class="btn btn-primary float-end" data-bs-toggle="modal" data-bs-target="#modal-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-side-by-side"> View Side-by-side </button> <div class="modal fade" id="modal-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-side-by-side" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-labelledby="staticBackdropLabel" aria-hidden="true">
251
+ <div class="modal-dialog modal-xl">
252
+ <div class="modal-content">
253
+ <div class="modal-header">
254
+ <h5 class="modal-title" id="modal-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-side-by-side-label">Side-by-side view</h5>
255
+ <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
256
+ </div>
257
+ <div class="modal-body">
258
+ <div class="container p-3">
259
+ <div class="row border">
260
+ <div class="col border">
261
+ <div class="p-3 mb-5 code highlight">
262
+ <span class="nb">require</span> <span class="s1">'paintbrush'</span><br /><br /><span class="kp">extend</span> <span class="no">Paintbrush</span><br /><br /><span class="n">output</span> <span class="o">=</span> <span class="n">paintbrush</span> <span class="k">do</span><br />  <span class="s2">"</span><span class="si">#{</span><span class="n">blue</span> <span class="s1">'foo'</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">green</span> <span class="s2">"bar </span><span class="si">#{</span><span class="n">cyan</span> <span class="sx">%w[foo bar baz]</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span><span class="si">}</span><span class="s2"> with </span><span class="si">#{</span><span class="n">cyan</span> <span class="s1">'qux'</span><span class="si">}</span><span class="s2"> and quux"</span><span class="si">}</span><span class="s2"> and corge"</span><br /><span class="k">end</span><br /><br /><span class="n">it_documents</span> <span class="n">output</span> <span class="k">do</span><br />  <span class="n">expect</span><span class="p">(</span><span class="n">output</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span> <span class="s2">"</span><span class="se">\e</span><span class="s2">[34mfoo</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m </span><span class="se">\e</span><span class="s2">[32mbar </span><span class="se">\e</span><span class="s2">[36mfoo, bar, baz"</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[32m with </span><span class="se">\e</span><span class="s2">[36mqux</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[32m "</span><span class="p">\</span><br />                        <span class="s2">"and quux</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m and corge"</span><br /><span class="k">end</span><br />
263
+ </div>
264
+ </div>
265
+ <div class="col border">
266
+ <div class="p-3 mb-5 code highlight">
267
+ <div class="ansi-html border m-1 p-4">
268
+ <span></span><span class="ansi-color-4">foo</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"> </span><span class="ansi-color-2">bar </span><span class="ansi-color-6">foo, bar, baz</span><span class="ansi-color-reset"></span><span class="ansi-color-2"> with </span><span class="ansi-color-6">qux</span><span class="ansi-color-reset"></span><span class="ansi-color-2"> and quux</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"> and corge</span>
269
+ </div>
270
+ </div>
271
+ </div>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ <div class="modal-footer">
276
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
277
+ </div>
278
+ </div>
279
+ </div>
280
+ </div>
281
+ <ul class="nav nav-tabs" id="html-tabs-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0" role="tablist">
282
+ <li class="nav-item" role="presentation"><button class="nav-link active" id="code-source-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-tab" data-bs-toggle="tab" data-bs-target="#code-source-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0" type="button" role="tab" aria-controls="code-source" aria-selected="true">Code</button></li>
283
+ <li class="nav-item" role="presentation"><button class="nav-link" id="rendered-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-tab" data-bs-toggle="tab" data-bs-target="#rendered-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0" type="button" role="tab" aria-controls="rendered" aria-selected="false">Output</button></li>
284
+ </ul>
285
+ <div class="tab-content" id="html-tab-content-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0">
286
+ <div class="tab-pane fade show active" id="code-source-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0" role="tabpanel" aria-labelledby="code-source-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-tab">
287
+ <div class="p-3 mb-5 code highlight">
288
+ <span class="nb">require</span> <span class="s1">'paintbrush'</span><br /><br /><span class="kp">extend</span> <span class="no">Paintbrush</span><br /><br /><span class="n">output</span> <span class="o">=</span> <span class="n">paintbrush</span> <span class="k">do</span><br />  <span class="s2">"</span><span class="si">#{</span><span class="n">blue</span> <span class="s1">'foo'</span><span class="si">}</span><span class="s2"> </span><span class="si">#{</span><span class="n">green</span> <span class="s2">"bar </span><span class="si">#{</span><span class="n">cyan</span> <span class="sx">%w[foo bar baz]</span><span class="p">.</span><span class="nf">join</span><span class="p">(</span><span class="s1">', '</span><span class="p">)</span><span class="si">}</span><span class="s2"> with </span><span class="si">#{</span><span class="n">cyan</span> <span class="s1">'qux'</span><span class="si">}</span><span class="s2"> and quux"</span><span class="si">}</span><span class="s2"> and corge"</span><br /><span class="k">end</span><br /><br /><span class="n">it_documents</span> <span class="n">output</span> <span class="k">do</span><br />  <span class="n">expect</span><span class="p">(</span><span class="n">output</span><span class="p">).</span><span class="nf">to</span> <span class="n">eql</span> <span class="s2">"</span><span class="se">\e</span><span class="s2">[34mfoo</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m </span><span class="se">\e</span><span class="s2">[32mbar </span><span class="se">\e</span><span class="s2">[36mfoo, bar, baz"</span> <span class="p">\</span><br />                        <span class="s2">"</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[32m with </span><span class="se">\e</span><span class="s2">[36mqux</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[32m "</span><span class="p">\</span><br />                        <span class="s2">"and quux</span><span class="se">\e</span><span class="s2">[0m</span><span class="se">\e</span><span class="s2">[0m and corge"</span><br /><span class="k">end</span><br />
289
+ </div>
290
+ </div>
291
+ <div class="tab-pane fade" id="rendered-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0" role="tabpanel" aria-labelledby="rendered-c3e2bbb8-9a13-4400-93a1-0e6c9847cba0-tab">
292
+ <div class="p-3 mb-5 code highlight">
293
+ <div class="ansi-html border m-1 p-4">
294
+ <span></span><span class="ansi-color-4">foo</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"> </span><span class="ansi-color-2">bar </span><span class="ansi-color-6">foo, bar, baz</span><span class="ansi-color-reset"></span><span class="ansi-color-2"> with </span><span class="ansi-color-6">qux</span><span class="ansi-color-reset"></span><span class="ansi-color-2"> and quux</span><span class="ansi-color-reset"></span><span class="ansi-color-reset"> and corge</span>
295
+ </div>
296
+ </div>
297
+ </div>
298
+ </div>
299
+ </div>
300
+
301
+ </div>
302
+ </div>
303
+ <hr/>
304
+
305
+ </div>
306
+
307
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.bundle.min.js"
308
+ integrity="sha512-i9cEfJwUwViEPFKdC1enz4ZRGBj8YQo6QByFTF92YXHi7waCqyexvRD75S5NVTsSiTv7rKWqG9Y5eFxmRsOn0A=="
309
+ crossorigin="anonymous"
310
+ referrerpolicy="no-referrer"></script>
311
+ </body>
312
+ </html>
@@ -0,0 +1,36 @@
1
+ # Paintbrush
2
+
3
+ Simple and concise string colorization for _Ruby_ without overloading `String` methods or requiring verbose class/method invocation.
4
+
5
+ _Paintbrush_ has zero dependencies and does not pollute any namespaces or objects outside of the `#paintbrush` method wherever you include the `Paintbrush` module.
6
+
7
+ Nesting is supported, allowing you to use multiple colors within the same string. The previous color is automatically restored.
8
+
9
+ ```rspec:ansi
10
+ require 'paintbrush'
11
+
12
+ extend Paintbrush
13
+
14
+ output = paintbrush { purple "You used #{green 'four'} #{blue "(#{cyan '4'})"} #{yellow 'colors'} today!" }
15
+ it_documents output do
16
+ expect(output).to eql "\e[35mYou used \e[32mfour\e[0m\e[35m " \
17
+ "\e[34m(\e[36m4\e[0m\e[34m)\e[0m\e[35m " \
18
+ "\e[33mcolors\e[0m\e[35m today!\e[0m\e[0m"
19
+ end
20
+ ```
21
+
22
+ ```rspec:ansi
23
+ require 'paintbrush'
24
+
25
+ extend Paintbrush
26
+
27
+ output = paintbrush do
28
+ "#{blue 'foo'} #{green "bar #{cyan %w[foo bar baz].join(', ')} with #{cyan 'qux'} and quux"} and corge"
29
+ end
30
+
31
+ it_documents output do
32
+ expect(output).to eql "\e[34mfoo\e[0m\e[0m \e[32mbar \e[36mfoo, bar, baz" \
33
+ "\e[0m\e[32m with \e[36mqux\e[0m\e[32m "\
34
+ "and quux\e[0m\e[0m and corge"
35
+ end
36
+ ```
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: paintbrush
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bob Farrell
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-05-24 00:00:00.000000000 Z
11
+ date: 2023-05-26 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Provides a set of encapsulated methods for nested colorization of strings.
14
14
  email:
@@ -28,10 +28,16 @@ files:
28
28
  - Rakefile
29
29
  - doc/example.png
30
30
  - lib/paintbrush.rb
31
+ - lib/paintbrush/bounded_color_element.rb
32
+ - lib/paintbrush/color_element.rb
31
33
  - lib/paintbrush/colorized_string.rb
32
34
  - lib/paintbrush/colors.rb
35
+ - lib/paintbrush/element_tree.rb
36
+ - lib/paintbrush/escapes.rb
33
37
  - lib/paintbrush/version.rb
34
38
  - paintbrush.gemspec
39
+ - rspec-documentation/bundle/introduction.html
40
+ - rspec-documentation/pages/Introduction.md
35
41
  - sig/paintbrush.rbs
36
42
  homepage: https://github.com/bobf/paintbrush
37
43
  licenses: []