term_color 0.0.2 → 0.0.3

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: 6271321446e52dfcff7f4ed2e996586653731e79103a94504910df3823b1ec48
4
- data.tar.gz: 4bde610a032d25a836096bb389163c66d2dcacb81ad7c605366fbbf0dab491f0
3
+ metadata.gz: 5ca92238dae771cacd8dd2165975e084ac10aac980cdb3ce36f52da3d76742e4
4
+ data.tar.gz: 2e800af8a7aefbc39895d29ac25a8dcc67501b9a48a7827d76fbc017ae6ae9fc
5
5
  SHA512:
6
- metadata.gz: d9d58ee125d900d753fe50574b61c5314bb9d78c5e2ec7a2e350bf9913f3ce3c00e7ffdd5cff49fe7c2075c2e85c3ea24073c9246dcd216c214c3210035e0062
7
- data.tar.gz: 70d3f8cebf1834534a991c8c82ab17975103b1c346f03fa8527ccf58d128a9613c0ff866e95fcd2c234b9008b0d6ddf8bf81d0c586be078032e696e7b5256643
6
+ metadata.gz: 3bc3d2a0e78014e06c21d0be5ed18a5ca081c712ee456080b4eddca3c216b75e5986d2b909807d6e286edbcb3236626183a5f4a9af61465659b3ca6fb2cc376f
7
+ data.tar.gz: dd06e1eba2e21496090f4681b7c4c79dcd80a5211dba25129cf106a181ce0df26cd68c1298c12afac53a20ec05546abac2025d8fc4be9f3751a0ec2e56f92cbf
data/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  Rule-based text coloring/styling library for terminal text
4
4
 
5
+ [![Gem Version](https://badge.fury.io/rb/term_color.svg)](https://badge.fury.io/rb/term_color)
6
+
5
7
  ## Overview
6
8
 
7
9
  ### Justifying Features
@@ -16,11 +18,40 @@ Rule-based text coloring/styling library for terminal text
16
18
 
17
19
  ## Concepts/Syntax
18
20
 
19
- Rules are grouped into `RuleSets`, which are represented by instances of {TermColor::RuleSet}.
21
+ Rules are grouped into `RuleSets`, which are represented by instances of {TermColor::RuleSet}. Rules are applied to text using tag strings indicating start and end of rule application. There is also a tag that can be used outside of rule application to fully reset all styling options to system default.
22
+
23
+ ### Tags
24
+
25
+ _The 'tag' strings used to specify start and end of ranges of text to apply a rule to can be customized in the `RuleSet` constructor. These details assume the defaults are used._
26
+
27
+ - Open: `{%<rule name>`
28
+ - Ex: `{%rule1`
29
+ - Close: `%}`
30
+ - Reset: `%@`
31
+ - Only usable outside of tags. Resets all styling to default even if a previously used rule is configured to keep styling applied after close
32
+
33
+ ```ruby
34
+ # Rules named :a and :b
35
+ "Unstyled {%a Styled with a %} Normal {%bStyled with b%} normal %@ fully reset"
36
+ ```
37
+
38
+ Rules can be nested. When an inner rule tag is closed, the styling of the any outer/unclosed tags will be re-applied from outer most to inner most.
39
+
40
+ ```ruby
41
+ "{%aRule A{%bRule A+B{%cRule A+B+C%}A+B%}A%} Normal"
42
+ ```
43
+
44
+ ### Rule Sets
20
45
 
21
- ### Rule Set
46
+ Rule Sets contain named styling rules, any override options you chose to use (`after` behavior, open/close/reset symbols). Instances of `RuleSet` also provide methods for processing and displaying text with style tag markup via methods like `print` and `printf`
22
47
 
23
- A set is constructed from a Hash of rule, where they keys are rule names and the values are the rule definition hashes.
48
+ Gemeral Usage:
49
+
50
+ ```ruby
51
+ rs = TermColor.create_rule_set(<rules hash>, <named options>)
52
+ ```
53
+
54
+ Constructor:
24
55
 
25
56
  ```ruby
26
57
  rules = {
@@ -33,6 +64,13 @@ rule_set = TermColor.create_rule_set(rules)
33
64
  rule_set = TermColor::RuleSet.new(rules)
34
65
  ```
35
66
 
67
+ #### Constructor Options
68
+
69
+ - `after` - Override default 'after' rule (controlling what gets reset after a style tag is closed). Default is `:auto`, which automatically determines what styling to removed based on the rule being closed
70
+ - `:reset` - Causes all colors and styling to be reset on close
71
+ - `:keep` - Causes nothing to get reset
72
+ - Custom Hash - Allows you to specify your own after rule as a hash containing properties valid for `after` sections (`[:fg,:bg,:reset,:enable,:disable]`)
73
+
36
74
  #### Applying to Text
37
75
 
38
76
  Once you've got a `RuleSet` instance, calling its `apply` or `print`/`printf` methods with a string parameter will give back a copy of that string with styles applied
@@ -43,63 +81,116 @@ __Methods__
43
81
  - `print` - {TermColor::RuleSet#print}
44
82
  - `printf` - {TermColor::RuleSet#printf}
45
83
 
46
- __Use__
84
+ ### Rule Definitions
47
85
 
48
- - To apply a style to a section of the input string, surround it with `%rule_name` and `%%`
49
- - E.g.: `"%titleTitle Text%%"`
50
- - `%%` indicates the end of rule application, after which any `after` rules will get applied
51
- - Including `%%` when no rule is active will apply the `default` rule's `:after` options, which can either be overridden in your rule set, or make use of the built-in version which simply resets all colors and text styling to system default
52
- - Rule application can be nested (`"%titleTitle of %otherBook%%%%"`)
86
+ A Rule definition is a hash consisting of two parts, `inside` and `after`. `inside` dictates styling that gets applied to text between the open and close tags for the rule, `after` allows you to override what happens when that tag closes (overrides default `after` rule as specified in constructor).
53
87
 
54
- ### Rule Definitions
88
+ The `after` part, when used, must always be a hash in the rule definition assigned to the key `:after`. Everything else will be automatically grouped into a `inside` key/value if not explicitly specified.
89
+
90
+ ```ruby
91
+ r = { inside: { fg: :red }, after: { keep: :fg }}
92
+ r = { fg: :red, bg: :blue }
93
+ r = { after: { reset: :all} }
94
+ ```
95
+
96
+ ### Rule Options/Actions
55
97
 
56
- _For more details on rule definitions, see {file:docs/rule_dsl.md Rule DSL} ([View on GitHub](https://github.com/vdtdev/term_color/blob/master/docs/rule_dsl.md))_
98
+ #### Colors
57
99
 
58
- Rule definitions are just hashes that include rule options. The included options can be divided into `:inside` (applied to text rule is applied to) and `:after` (applied to text following text rule is applied to). If neither of these sub hashes are included, all options are treated as being for `:inside`, and an `:after` set is auto-generated to unapply style changes made inside rule for following text. To prevent an `:after` section from being automatically generated, either include your own `:after` section or include `after: {}`.
100
+ ##### Attributes
101
+
102
+ - `fg` - Change foreground color
103
+ - `bg` - Change background color
104
+
105
+ ##### Values
106
+
107
+ ###### Standard Named Colors
108
+
109
+ Color values can be color-name symbols as defined in {TermColor::Rule::Colors} (`:black, :red, :yellow, :blue, :magenta, :cyan, :white`)
110
+
111
+ ###### XTerm 256 Color Values
112
+
113
+ To use XTerm 256 Color Mode values, include the color code integer inside a single item array. (E.g. for code `208`, use `[208]`)
114
+
115
+ ###### XTerm 16m Color Values
116
+
117
+ To use XTerm 16m Color Mode RGB colors, include the red, green and blue color values in an ordered array (E.g. for 80 red, 80 green, 255 blue, use `[80,80,255]`)
118
+
119
+ #### Styles
120
+
121
+ ##### Actions
122
+
123
+ - `enable` - Style(s) to enable (Can be single item or array)
124
+ - `disable` - Style(s) to disable (Can be single item or array)
125
+
126
+ ##### Values
127
+
128
+ (See symbols in {TermColor::Rule::Styles})
129
+
130
+ - `:bold`/`:intense`
131
+ - `:dim`/`:dark`
132
+ - `:italic`
133
+ - `:underline`
134
+ - `:inverse`
135
+ - `:hidden`
136
+ - `:strikethrough`
137
+
138
+ #### Reset / Keep
139
+
140
+ _(Only valid in `after` section)_
141
+
142
+ Quick way of resetting one or more style rules. `reset`/`keep` can be given a single symbol or an array of symbols.
143
+
144
+ `keep` will be ignored if included in default after rule
59
145
 
60
146
  ```ruby
61
- # No groups, after will be generated
62
- rule = { fg: :red }
63
- # Same as above but with groups. after will be generated
64
- rule = { inside: { fg: :red } }
65
- # No groups, skip after generation by setting it to
66
- # empty hash of options
67
- rule = { fg: :red, after: {} }
68
- # Both groups, same as others but with explicitly set after options,
69
- # no auto-generation
70
- rule = { inside: { fg: :red }, after: { reset: :fg } }
147
+ { reset: [options] }
148
+ { keep: [options] }
71
149
  ```
72
150
 
73
- For more details on rule definitions, see {file:docs/rule_dsl.md Rule DSL} ([View on GitHub]{https://github.com/vdtdev/term_color/blob/master/docs/rule_dsl.md})
151
+ #### Options
74
152
 
75
- ## Examples
153
+ - `:fg` / `:bg` - Reset foreground / background color
154
+ - `:style` - Reset all styling
155
+ - `:all` - Reset all colors and styling
76
156
 
77
157
  [If example images are missing, view readme on github](https://github.com/vdtdev/term_color/blob/master/README.md)
78
158
 
79
- ### Basic
159
+ ## Examples
160
+
161
+ _If images don't show up, try viewing on [Github](https://github.com/vdtdev/term_color/blob/master/README.md)_
162
+
163
+ ### Basic w/ Nesting (`:auto` after mode)
80
164
 
81
165
  ```ruby
82
166
  rule_set = TermColor.create_rule_set({
83
- opt: { fg: :cyan },
84
- err: { fg: :red }
167
+ yellow: { fg: :yellow },
168
+ ul: { enable: :ul },
169
+ err: { fg: :white, bg: :red, enable: [:italic] }
85
170
  })
86
171
 
87
- rule_set.printf "%errInvalid%% option: %opt%s%%\n", "fruit"
172
+ rule_set.print "Test Score: {%yellow65%}\n"
173
+ rule_set.print "{%ul{%err{%yellowNOTE:%}" +
174
+ "Your score is below a passing grade%}X%}\n"
88
175
  ```
89
176
 
90
177
  ![](./file/docs/example_1.png)
91
178
  ![](./docs/example_1.png)
92
179
 
93
- ### Nested /w XTerm RGB
180
+ ### With `:keep` after mode
94
181
 
95
182
  ```ruby
96
183
  rule_set = TermColor.create_rule_set({
97
- title: { fg: :yellow, enable: :underline },
98
- emph: { fg: [0xa0,0xa0,0xff], enable: :italic },
99
- author: { fg: :green, bg: :blue, enable: :bold }
100
- })
101
-
102
- rule_set.print "book: %%titleHarry Potter (%emph%%)%% by %authorJ. K. Rowling%%\n"
184
+ g: { fg: :green },
185
+ i: { enable: :inverse },
186
+ f: { fg: :yellow, after: { reset: :fg }}
187
+ }, after: :keep)
188
+
189
+ rule_set.print "{%gTitle%}\n"
190
+ rule_set.print "{%i>%}"
191
+ (1..5).each {|i| print "#{i}\n" }
192
+ rule_set.print "{%fThis style reverts after closing%}\n"
193
+ rule_set.print "Before Reset%@After Reset\n"
103
194
  ```
104
195
 
105
196
  ![](./file/docs/example_2.png)
@@ -12,9 +12,9 @@ module TermColor
12
12
  # # Insize: (`a:`) Foreground yellow, bg red, dark style on
13
13
  # # After: Resets all color and style options to default,
14
14
  # # including those set by other rules
15
- # rule = {
15
+ # rule = {
16
16
  # a: {
17
- # fg: :yellow, bg: :red, enable: :dark
17
+ # fg: :yellow, bg: :red, enable: :dark
18
18
  # },
19
19
  # z: {
20
20
  # reset: :all
@@ -45,23 +45,23 @@ module TermColor
45
45
  ##
46
46
  # Numerical modifiers used with Color Values
47
47
  # to target foreground or background.
48
- #
48
+ #
49
49
  # - For {Colors Named Standard Colors}, value is added to given
50
50
  # color's numerical value
51
51
  # - For XTerm 256/16m color codes, value is added to mode base
52
- #
52
+ #
53
53
  # @example Named Standard Color Background
54
54
  # { bg: :red } #=> 40 + 1 = 41
55
55
  # @example XTerm 256 Foreground
56
56
  # { fg: [208] } #=> 8 + 30 = 38
57
- ColorTargets = {
57
+ ColorTargets = {
58
58
  fg: 30, # Foreground target
59
59
  bg: 40 # Background target
60
60
  }.freeze
61
61
 
62
62
  ##
63
63
  # Style option constants
64
- # (Values that can be included in style `enable` and `disable`
64
+ # (Values that can be included in style `enable` and `disable`
65
65
  # rule option attributes)
66
66
  Styles = {
67
67
  bold: 1,
@@ -75,6 +75,7 @@ module TermColor
75
75
  italic: 3,
76
76
  underline: 4,
77
77
  inverse: 7,
78
+ hidden: 8,
78
79
  strikethrough: 9
79
80
  }.freeze
80
81
 
@@ -85,12 +86,12 @@ module TermColor
85
86
  # was used)
86
87
  # @example Disable italic
87
88
  # (:disable) + (:italic) #=> 20 + 3 = 23
88
- StyleActions = {
89
+ StyleActions = {
89
90
  # Enable style(s) action
90
91
  enable: 0,
91
92
  # Disable style(s) action
92
- disable: 20
93
- }
93
+ disable: 20
94
+ }.freeze
94
95
 
95
96
  ##
96
97
  # Reset option constants
@@ -101,9 +102,17 @@ module TermColor
101
102
  # Reset foreground color only
102
103
  fg: 39,
103
104
  # Reset background color only
104
- bg: 49
105
+ bg: 49,
106
+ # Reset style
107
+ style: StyleActions[:disable]
105
108
  }.freeze
106
109
 
110
+ ##
111
+ # Operations associated with reset
112
+ ResetOps = [ :reset, :keep ].freeze
113
+
114
+ ResetsExtra = [ :style ].freeze
115
+
107
116
  ##
108
117
  # Descriptive aliases for part names
109
118
  Parts = {
@@ -111,7 +120,7 @@ module TermColor
111
120
  inside: :inside,
112
121
  # Style appled when rule close is given
113
122
  after: :after
114
- }
123
+ }.freeze
115
124
 
116
125
  ##
117
126
  # Valid rule operations mapped to accepted const values
@@ -126,13 +135,38 @@ module TermColor
126
135
  # Disable style(s) action
127
136
  disable: Styles.keys,
128
137
  # Reset action
129
- reset: [
130
- :fg, # Reset fg color
131
- :bg, # Reset bg color
132
- :style, # Reset all styles
133
- :all # Reset colors and styles
134
- ]
135
- }
138
+ reset: Resets.keys,
139
+ # Keep action (opposite of reset)
140
+ keep: Resets.keys
141
+ }.freeze
142
+
143
+ ##
144
+ # Normalize rules for ops; either `:keep` to
145
+ # not change original value, or `:array` to
146
+ # wrap single value inside array
147
+ OpNormalize = {
148
+ fg: :keep,
149
+ bg: :keep,
150
+ enable: :array,
151
+ disable: :array,
152
+ reset: :array,
153
+ keep: :array
154
+ }.freeze
155
+
156
+ ##
157
+ # Allowed ops by part
158
+ PartOps = {
159
+ inside: [:fg, :bg, :enable, :disable],
160
+ after: [:fg, :bg, :enable, :disable, :reset, :keep]
161
+ }.freeze
162
+
163
+ ##
164
+ # Operations allowed within 'after'
165
+ AfterOps = (Ops.filter{|k,v| PartOps[:after].include?(k)}).freeze
166
+
167
+ ##
168
+ # Operations allowed within 'inside'
169
+ InsideOps = (Ops.filter {|k,v| PartOps[:inside].include?(k)}).freeze
136
170
 
137
171
  ##
138
172
  # Value added to ColorTarget when using XTerm colors
@@ -167,10 +201,13 @@ module TermColor
167
201
  ##
168
202
  # Compile rule into frozen instance of `Compiled` struct
169
203
  # @param [Hash] rule Rule hash
204
+ # @param [RuleSet] rs Rule set
205
+ # @param [Boolean] is_reset Set to true to indicate rule is for reset
206
+ # operation, and should ignore default after resolution
170
207
  # @return [Compiled] Frozen instance of `Compiled` struct
171
208
  # containing compiled rule
172
- def compile(rule)
173
- evaluated = evaluate(rule)
209
+ def compile(rule, rs, is_reset=false)
210
+ evaluated = evaluate(rule,rs,is_reset)
174
211
  return Compiled.new(
175
212
  rule,
176
213
  evaluated,
@@ -201,16 +238,16 @@ module TermColor
201
238
  end
202
239
  end
203
240
  codes = codes.flatten.compact.uniq
204
- end
241
+ end
205
242
 
206
243
  def resolve_color(color, target = :fg)
207
244
  if color.is_a?(Array)
208
245
  color = color[0..2]
209
246
  return xterm_color(color, target)
210
247
  end
211
-
248
+
212
249
  if !color.is_a?(Integer)
213
-
250
+
214
251
  if color.is_a?(Hash) && ColorsAdvanced.keys.include?(color.keys[0])
215
252
  return self.method(ColorsAdvanced[color.keys[0]]).call(color.values[0])
216
253
  end
@@ -223,7 +260,10 @@ module TermColor
223
260
  if !style.is_a?(Integer)
224
261
  style = Styles[style.to_sym].to_i
225
262
  end
226
- (style + StyleActions[state.to_sym].to_i)
263
+ s_code = (style + StyleActions[state.to_sym].to_i)
264
+ # adjust so bold and dim both get 22
265
+ s_code = [s_code, StyleActions[:disable] + 2].max if state == :disable
266
+ return s_code
227
267
  end
228
268
 
229
269
  def resolve_reset(target)
@@ -257,15 +297,16 @@ module TermColor
257
297
  # Evaluate rule, returning new hash containing list of numerical
258
298
  # codes to use for inside (`:inside`) and after (`:after`)
259
299
  # @param [Hash] rule Rule hash to evaluate
300
+ # @param [RuleSet] rs Rule set
260
301
  # @return [Hash] evaluated version of rule, containing code numbers
261
- def evaluate(rule)
302
+ def evaluate(rule, rs, is_reset)
262
303
  # error if not hash
263
304
  return nil if !rule.is_a?(Hash)
264
305
 
265
306
  inside_part_key = Parts[:inside]
266
307
  after_part_key = Parts[:after]
267
308
  rule_keys = rule.keys.map{|k|k.to_sym}
268
-
309
+
269
310
  # Find 'inside' rule options
270
311
  if rule_keys.include?(inside_part_key)
271
312
  # 'inside' key explicitly defined, so pull from that
@@ -279,15 +320,15 @@ module TermColor
279
320
  # Find 'after' rule options, using nil if not present
280
321
  # This means that if it is defined but as an empty hash,
281
322
  # no 'after' rule options will be auto-generated
282
- after_part = rule.fetch(after_part_key, nil)
283
-
284
- # Auto-generate 'after' rule options if not explicitly defined
285
- if after_part.nil?
286
- resets = inside_part.keys.filter { |k| ColorTargets.keys.include?(k) }
287
- disables = inside_part.fetch(:enable, [])
288
- after_part = {}
289
- after_part[:reset] = resets if resets.length > 0
290
- after_part[:disable] = disables if disables.length > 0
323
+ after_part = rule.fetch(after_part_key, {})
324
+
325
+ # Resolve after, either from template mixed with any
326
+ # overrides or an automatically generated version mixed with
327
+ # overrides
328
+ if rs.default_after == :auto
329
+ after_part = build_auto_after(inside_part, after_part)
330
+ else
331
+ after_part = override_after(inside_part, after_part, rs.default_after)
291
332
  end
292
333
 
293
334
  parts = {}
@@ -296,9 +337,100 @@ module TermColor
296
337
  parts[after_part_key] = evaluate_ops(after_part)
297
338
 
298
339
  return parts.merge({evaluated: true})
340
+ end
341
+
342
+ def normalize_part(hash,part,clean=false)
343
+ h = hash.dup
344
+ PartOps[part].each do |o|
345
+ if OpNormalize[o] == :array
346
+ h[o] = [h.fetch(o,[])].flatten
347
+ else
348
+ h[o] = h.fetch(o,nil)
349
+ end
350
+ end
351
+
352
+ if clean
353
+ h = h.filter {|k,v| (v.is_a?(Array))? v.length > 0 : !v.nil? }
354
+ end
355
+
356
+ return h
357
+ end
358
+
359
+ def override_after(inside, override, after)
360
+ c_ovr = normalize_part(override, :after)
361
+ c_aft = normalize_part(after, :after)
362
+ c_inside = normalize_part(inside, :inside)
363
+
364
+ # change reset :all to specific resets
365
+ if c_aft[:reset].include?(:all)
366
+ c_aft[:reset] = Resets.keys - [:all]
367
+ end
368
+ # change keep :all to specific resets
369
+ if c_ovr[:keep].include?(:all)
370
+ c_ovr[:keep] = Resets.keys - [:all]
371
+ end
372
+
373
+ c_aft[:reset] = (c_aft[:reset] + c_ovr[:reset]).uniq
374
+ # remove keeps from resets
375
+ c_aft[:reset] -= c_ovr[:keep]
376
+
377
+ # clear disable if keep :style
378
+ if c_ovr[:keep].include?(:style) ||
379
+ c_aft[:disable] = []
380
+ end
381
+
382
+ # if override disables styles, remove blanket style reset
383
+ if c_ovr[:disable].length >= 1
384
+ c_aft[:reset] -= [:style]
385
+ end
386
+
387
+ # replace reset :style with style specific targets
388
+ if c_inside[:enable].length > 0 && c_aft[:reset].include?(:style)
389
+ c_aft[:reset] -= [:style]
390
+ c_aft[:disable] += c_inside[:enable]
391
+ c_aft[:enable] -= c_inside[:enable]
392
+ end
299
393
 
394
+ # If we've reached this point with reset :style, change it into
395
+ # disables
396
+ if c_aft[:reset].include?(:style)
397
+ c_aft[:reset] -= [:style]
398
+ c_aft[:disable] += Styles.keys
399
+ end
400
+
401
+ # prevent enables from conflicting with disables
402
+ en = (c_aft[:enable] + c_ovr[:enable] - c_ovr[:disable]).uniq
403
+ di = (c_aft[:disable] + c_ovr[:disable] - c_ovr[:enable]).uniq
404
+
405
+ en -= di
406
+ di -= en
407
+
408
+ result = { enable: en, disable: di, reset: c_aft[:reset] }
409
+ ColorTargets.keys.each do |k|
410
+ val = (c_ovr[k] || c_aft[k])
411
+ result[k] = val unless val.nil?
412
+ end
413
+
414
+ return normalize_part(result, :after, true)
300
415
  end
301
416
 
417
+ def build_auto_after(inside, after={})
418
+ c_inside = normalize_part(inside, :inside)
419
+ n_after = normalize_part({}, :after)
420
+
421
+ if c_inside[:enable].length > 0
422
+ n_after[:reset] += [:style]
423
+ end
424
+
425
+ ColorTargets.keys.each do |k|
426
+ if !c_inside[k].nil?
427
+ n_after[:reset] += [k]
428
+ end
429
+ end
430
+
431
+ override_after(inside, after, n_after)
432
+ end
433
+
302
434
  ##
303
435
  # Return ANSI color codes from evaluated rule
304
436
  # @param [Hash] rule Full rule
@@ -313,4 +445,4 @@ module TermColor
313
445
  }
314
446
  end
315
447
  end
316
- end
448
+ end
@@ -5,24 +5,60 @@ module TermColor
5
5
  # @license MIT
6
6
  class RuleSet
7
7
 
8
+ DBG = false
9
+
8
10
  ##
9
- # Symbol used as prefix for rule name to denote rule start
10
- RULE_SYMBOL='%'
11
+ # Default rule symbols
12
+ DEFAULT_SYMBOLS = {
13
+ open: '{%',
14
+ close: '%}',
15
+ reset: '%@'
16
+ }.freeze
17
+
11
18
  ##
12
- # String used to denote rule close / reset
13
- RESET_SYMBOL='%%'
19
+ # Default reset rule
20
+ DEFAULT_RESET_RULE = {after: {reset: :all} }.freeze
14
21
 
15
- DEFAULT_RESET_RULE = {z: {reset: :all} }
22
+ ##
23
+ # After preset options
24
+ AFTER_PRESETS = {
25
+ # Full reset
26
+ reset: {reset: :all},
27
+ # Automatically determine what to toggle off
28
+ auto: :auto,
29
+ # No reset
30
+ keep: {keep: :all}
31
+ }
16
32
 
17
- attr_reader :rules, :regexs
33
+ ##
34
+ # Struct for rule and reset symbols
35
+ SymbolOptions = Struct.new(:open,:close,:reset)
36
+
37
+ ##
38
+ # Default after preset choice
39
+ DEFAULT_AFTER = :auto
40
+
41
+ attr_reader :rules, :regexs, :default_after, :symbols
18
42
 
19
43
  ##
20
44
  # Construct new rule set
21
45
  # @param [Hash] rules Hash of rule names mapping to rule hashes,
22
- # which can define before rules (`a:`), after rules (`z:`) or both.
23
- # - If neither are given, content is treated as though it was inside a `a:` key.
24
- # - If `a:` only is given, {TermColor::Rule#evaluate Rule evaluate method} attempts to
25
- # auto guess `z:`, resetting any used color or style rules from `a:`
46
+ # which can define before rules (`inside:`), after rules (`after:`) or both.
47
+ # - If neither are given, content is treated as though it was inside a `inside:` key.
48
+ # - If `inside:` only is given, {TermColor::Rule#evaluate Rule evaluate method} attempts to
49
+ # auto guess `after:`, resetting any used color or style rules from `inside:`
50
+ # @param [Hash] opts Optional arguments
51
+ # @option opts [Hash|Symbol] :after Override default `:after` rule behavior when rule has no `after:`
52
+ # Options:
53
+ # - `:reset` - Reset all color and styles
54
+ # - `:auto` (default) - Try to automatically determine what to reset based on applied colors/styles
55
+ # - `:keep` - Keel all rule styles intact
56
+ # - (`Hash`) - Custom rule (formatted as Rule `after` prop, e.g. `{ reset: :fg, keep: :style }`)
57
+ # @option opts [Hash] :symbols Override styling symbols
58
+ # Options:
59
+ # - `:open` - Rule open symbol (used as symbolRulename) (default `{%`)
60
+ # - `:close` - Rule close symbol (default `%}`)
61
+ # - `:reset` - Symbol that can be used between rule blocks to fully reset everything (default `%@`)
26
62
  # @see TermColor::Rule
27
63
  # @example
28
64
  # rules = RuleSet.new({
@@ -33,10 +69,10 @@ module TermColor
33
69
  # quote: { enable: :italic },
34
70
  # # A weird rule that will make fg red inside rule,
35
71
  # # and change fg to blue after rule block ends
36
- # weird: { a: { fg: :red }, z: { fg: :blue }}
72
+ # weird: { inside: { fg: :red }, after: { fg: :blue }}
37
73
  # })
38
74
  #
39
- # print rules.colorize("%nameJohn%%: '%%quoteRoses are %%weirdRed%% (blue)%%.\n")
75
+ # print rules.colorize("{%nameJohn%}: '{%quoteRoses are {%weirdRed%} (blue)%}.\n")
40
76
  # # Result will be:
41
77
  # # fg green+underline "John"
42
78
  # # regular ": "
@@ -44,9 +80,23 @@ module TermColor
44
80
  # # fg red (still italic) "Red"
45
81
  # # (fg blue)(still italic) "(blue)"
46
82
  # # (regular) "."
47
- def initialize(rules)
83
+ def initialize(rules={}, **opts)
84
+ if rules.nil?
85
+ rules = opts
86
+ opts = {}
87
+ end
48
88
  @base_rules = rules
49
- @base_rules[:default] = @base_rules.fetch(:default, DEFAULT_RESET_RULE)
89
+ @base_rules[:reset] = @base_rules.fetch(:reset, DEFAULT_RESET_RULE)
90
+ # binding.pry
91
+ after = opts.fetch(:after, nil)
92
+ after = DEFAULT_AFTER if after.nil? || (after.is_a?(Symbol) && !AFTER_PRESETS.has_key?(after))
93
+ @default_after = (after.is_a?(Hash))? after : AFTER_PRESETS[after]
94
+ sym_opts = opts.fetch(:symbols,{})
95
+ @symbols = SymbolOptions.new(
96
+ sym_opts.fetch(:open, DEFAULT_SYMBOLS[:open]),
97
+ sym_opts.fetch(:close, DEFAULT_SYMBOLS[:close]),
98
+ sym_opts.fetch(:reset, DEFAULT_SYMBOLS[:reset])
99
+ )
50
100
  evaluate_rules
51
101
  build_regexs
52
102
  end
@@ -57,33 +107,54 @@ module TermColor
57
107
  # @return [String] Text with ANSI style codes injected
58
108
  def apply(text)
59
109
  raw = process_text(text)
60
- last_rule = nil
110
+ rule_stack = []
61
111
  str = ''
112
+ rule_names = @rules.keys
62
113
  raw.each do |r|
63
- if r.is_a?(Symbol)
64
- # if (r == :close_rule && !last_rule.nil?)
65
- # str.concat(Rule.codes(@rules[last_rule][:z]))
66
- # last_rule = nil
67
- # elsif r == :default
68
- # str.concat(Rule.codes(@rules[r][:z]))
69
- # last_rule = nil
70
- # else
71
- # last_rule = r
72
- # str.concat(Rule.codes(@rules[r][:a]))
73
- # end
74
- if (r == :default) && !last_rule.nil?
75
- str.concat(@rules[last_rule].codes(Rule::Parts[:after]))
76
- last_rule = nil
77
- elsif r == :default
78
- str.concat(@rules[r].codes(Rule::Parts[:after]))
79
- last_rule = nil
80
- else
81
- last_rule = r
82
- str.concat(@rules[r].codes(Rule::Parts[:inside]))
114
+ if r.is_a?(Symbol)
115
+ # Part is a rule
116
+ dprint "\tRule Symbol #{r}\n"
117
+ if r == :close && rule_stack.length >= 1
118
+ # Rule close with 1+ opened rules
119
+ opened = rule_stack.pop
120
+ opened_after = @rules[opened].codes(Rule::Parts[:after])
121
+ dprint "\t\tClose, opened rule '#{opened}'\n"
122
+ dprint "\t\t\tClosing rule '#{opened}' with After\n"
123
+ dprint 4,"After: #{opened_after.inspect}\n"
124
+ str.concat(opened_after)
125
+ unless rule_stack.length == 0
126
+ rule_stack.each do |outer|
127
+ outer_inside = @rules[outer].codes(Rule::Parts[:inside])
128
+ # Closed rule was nested in another open rule
129
+ dprint 3, "Outer rule '#{outer}' still open. Restoring Inside\n"
130
+ dprint 4, "Inside: #{outer_inside.inspect}\n}"
131
+ str.concat(outer_inside)
83
132
  end
84
- else
85
- str.concat(r)
133
+ end
134
+ # binding.pry
135
+ # outer = rule_stack[-1]
136
+ # outer_inside = @rules[outer].codes(Rule::Parts[:inside])
137
+ # # Closed rule was nested in another open rule
138
+ # dprint 3, "Outer rule '#{outer}' still open. Restoring Inside\n"
139
+ # dprint 4, "Inside: #{outer_inside.inspect}\n}"
140
+ # str.concat(outer_inside)
141
+ # # binding.pry
142
+ # end
143
+ elsif r == :reset && rule_stack.length == 0
144
+ # no opened outer rules, reset symbol given
145
+ dprint "\t\tReset, no opened rule\n"
146
+ str.concat(@rules[r].codes(Rule::Parts[:after]))
147
+ elsif rule_names.include?(r)
148
+ # New rule to apply
149
+ dprint "\t\tApplying new rule '#{r}'\n"
150
+ dprint 3, "Previous active rule `#{rule_stack[-1]}`\n"
151
+ rule_stack.push r
152
+ str.concat(@rules[r].codes(Rule::Parts[:inside]))
86
153
  end
154
+ else
155
+ # Part is text
156
+ str.concat(r)
157
+ end
87
158
  end
88
159
  str
89
160
  end
@@ -104,7 +175,7 @@ module TermColor
104
175
  # Wraps STDOUT printf method, passing output of `apply` to `print`
105
176
  # Doesn't actually use `printf`, instead passes result of
106
177
  # `format_string % args` to `print`.
107
- # @param [String] format_string printf format string,
178
+ # @param [String] format_string printf format string,
108
179
  # including TermColor style tags
109
180
  # @param [Array] args printf values to use with format string
110
181
  # @param [Hash] opts Optional params
@@ -115,23 +186,35 @@ module TermColor
115
186
 
116
187
  # Sanitize rule symbols
117
188
  sanitized = format_string.dup
118
- @rules.keys.each { |k| sanitized.gsub!("#{RULE_SYMBOL}#{k.to_s}","#{255.chr}#{k.to_s}") }
119
- sanitized.gsub!(RESET_SYMBOL, 255.chr*2)
120
-
189
+ @rules.keys.each { |k| sanitized.gsub!("#{@symbols.rule}#{k.to_s}","#{255.chr}#{k.to_s}") }
190
+ sanitized.gsub!(@symbols.reset, 255.chr*2)
191
+
121
192
  t = sanitized % args
122
193
  # Reinstate rule symbols
123
- @rules.keys.each { |k| t.gsub!("#{255.chr}#{k.to_s}","#{RULE_SYMBOL}#{k.to_s}") }
124
- t.gsub!(255.chr*2,RESET_SYMBOL)
125
-
194
+ @rules.keys.each { |k| t.gsub!("#{255.chr}#{k.to_s}","#{@symbols.rule}#{k.to_s}") }
195
+ t.gsub!(255.chr*2,@symbols.reset)
196
+
126
197
  stdout.print apply(t)
127
198
  end
128
-
199
+
129
200
  private
130
201
 
202
+ def dprint(*v)
203
+ if DBG
204
+ if v.length == 2
205
+ tc,t=v
206
+ tabs = "\t" * tc
207
+ print "#{tabs}#{t}"
208
+ else
209
+ print v[0]
210
+ end
211
+ end
212
+ end
213
+
131
214
  def evaluate_rules
132
215
  @rules = {}
133
216
  @base_rules.each_pair do |k,v|
134
- @rules[k] = Rule.compile(v)
217
+ @rules[k] = Rule.compile(v, self)
135
218
  end
136
219
  end
137
220
 
@@ -140,11 +223,14 @@ module TermColor
140
223
  src = @rules
141
224
  src.each_pair do |k,v|
142
225
  @regexs[k] = Regexp.compile(
143
- "(?<#{k.to_s}>(#{RULE_SYMBOL}#{k.to_s}))"
226
+ "(?<#{k.to_s}>(#{@symbols.open}#{k.to_s}))"
144
227
  )
145
228
  end
146
- @regexs[:default] = Regexp.compile(
147
- "(?<default>(#{RESET_SYMBOL}))"
229
+ @regexs[:close] = Regexp.compile(
230
+ "(?<default>(#{@symbols.close}))"
231
+ )
232
+ @regexs[:reset] = Regexp.compile(
233
+ "(?<default>(#{@symbols.reset}))"
148
234
  )
149
235
  end
150
236
 
@@ -203,7 +289,7 @@ module TermColor
203
289
  if !is_last
204
290
  end_pos = locations[i+1][:begin] - 1
205
291
  end
206
-
292
+
207
293
  working << l[:symbol]
208
294
  working << text[l[:continue_pos]..end_pos]
209
295
  end
@@ -211,4 +297,4 @@ module TermColor
211
297
  end
212
298
 
213
299
  end
214
- end
300
+ end
data/lib/term_color.rb CHANGED
@@ -9,8 +9,10 @@ module TermColor
9
9
  ##
10
10
  # Alias for constructing a new RuleSet
11
11
  # @see TermColor::RuleSet
12
- def create_rule_set(rules={})
13
- TermColor::RuleSet.new(rules)
12
+ def create_rule_set(rules=nil,**opts)
13
+ TermColor::RuleSet.new(rules,opts)
14
14
  end
15
15
 
16
- end
16
+
17
+
18
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: term_color
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Wade H.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-26 00:00:00.000000000 Z
11
+ date: 2020-04-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -46,7 +46,7 @@ licenses:
46
46
  - MIT
47
47
  metadata:
48
48
  source_code_uri: https://github.com/vdtdev/term_color
49
- documentation_uri: https://rubydoc.info/gems/term_color/0.0.2
49
+ documentation_uri: https://rubydoc.info/gems/term_color/0.0.3
50
50
  post_install_message:
51
51
  rdoc_options: []
52
52
  require_paths: