paintbrush 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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: []