terminal-layout 0.4.0 → 0.4.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +3 -1
- data/lib/terminal_layout/version.rb +1 -1
- data/lib/terminal_layout.rb +1 -1
- data/terminal-layout.gemspec +1 -0
- metadata +16 -5
- data/lib/ansi_string.rb +0 -371
- data/spec/ansi_string_spec.rb +0 -701
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 97802dee983f8248f8d8406cf323754b8768613a
|
4
|
+
data.tar.gz: 7af99e4ca121f6cf67668985954e2f52bb26aa87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5698f4d780074e3b2d10540a7dbbb27f7d0f6046ac93826ca86be379aa43266c997826b5723c0605788bc9b67080c9f4715cc647dd021d2f35e4eef8a08762e7
|
7
|
+
data.tar.gz: cf51538f560efbd170bb726cef284adebbede5599ae418f626f011447a2fcbadefe745c501c0a8187770c1104c344f2907238ff70ecfab5a19043e3d4fcd687e
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
terminal-layout (0.4.
|
4
|
+
terminal-layout (0.4.1)
|
5
|
+
ansi_string (~> 0.1)
|
5
6
|
highline (~> 1.7, >= 1.7.8)
|
6
7
|
ruby-terminfo (~> 0.1.1)
|
7
8
|
ruby-termios (~> 0.9.6)
|
@@ -10,6 +11,7 @@ PATH
|
|
10
11
|
GEM
|
11
12
|
remote: https://rubygems.org/
|
12
13
|
specs:
|
14
|
+
ansi_string (0.1.0)
|
13
15
|
byebug (5.0.0)
|
14
16
|
columnize (= 0.9.0)
|
15
17
|
coderay (1.1.0)
|
data/lib/terminal_layout.rb
CHANGED
data/terminal-layout.gemspec
CHANGED
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
+
spec.add_dependency 'ansi_string', '~> 0.1'
|
21
22
|
spec.add_dependency "ruby-terminfo", "~> 0.1.1"
|
22
23
|
spec.add_dependency "ruby-termios", "~> 0.9.6"
|
23
24
|
spec.add_dependency 'highline', '~> 1.7', '>= 1.7.8'
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: terminal-layout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Dennis
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ansi_string
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.1'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.1'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: ruby-terminfo
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -129,11 +143,9 @@ files:
|
|
129
143
|
- README.md
|
130
144
|
- Rakefile
|
131
145
|
- block-flow.rb
|
132
|
-
- lib/ansi_string.rb
|
133
146
|
- lib/tasks/gem.rake
|
134
147
|
- lib/terminal_layout.rb
|
135
148
|
- lib/terminal_layout/version.rb
|
136
|
-
- spec/ansi_string_spec.rb
|
137
149
|
- spec/spec_helper.rb
|
138
150
|
- spec/terminal_layout_spec.rb
|
139
151
|
- terminal-layout.gemspec
|
@@ -163,7 +175,6 @@ signing_key:
|
|
163
175
|
specification_version: 4
|
164
176
|
summary: A terminal layout manager
|
165
177
|
test_files:
|
166
|
-
- spec/ansi_string_spec.rb
|
167
178
|
- spec/spec_helper.rb
|
168
179
|
- spec/terminal_layout_spec.rb
|
169
180
|
has_rdoc:
|
data/lib/ansi_string.rb
DELETED
@@ -1,371 +0,0 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
|
-
require 'forwardable'
|
4
|
-
|
5
|
-
class ANSIString
|
6
|
-
extend Forwardable
|
7
|
-
attr_reader :raw, :without_ansi
|
8
|
-
|
9
|
-
def_delegators :@without_ansi, :each_char, :each_byte, :index,
|
10
|
-
:match, :=~
|
11
|
-
|
12
|
-
def initialize(str)
|
13
|
-
process_string raw_string_for(str)
|
14
|
-
end
|
15
|
-
|
16
|
-
def +(other)
|
17
|
-
self.class.new @raw + raw_string_for(other)
|
18
|
-
end
|
19
|
-
|
20
|
-
def <<(other)
|
21
|
-
range = length..length
|
22
|
-
str = replace_in_string(range, other)
|
23
|
-
process_string raw_string_for(str)
|
24
|
-
self
|
25
|
-
end
|
26
|
-
|
27
|
-
def insert(position, string)
|
28
|
-
if position < 0
|
29
|
-
position = @without_ansi.length + position + 1
|
30
|
-
end
|
31
|
-
self[position...position] = string
|
32
|
-
self
|
33
|
-
end
|
34
|
-
|
35
|
-
def empty?
|
36
|
-
length == 0
|
37
|
-
end
|
38
|
-
|
39
|
-
def [](range)
|
40
|
-
# convert numeric position to a range
|
41
|
-
range = (range..range) if range.is_a?(Integer)
|
42
|
-
|
43
|
-
range_begin = range.begin
|
44
|
-
range_end = range.end
|
45
|
-
|
46
|
-
if range.exclude_end?
|
47
|
-
if range_begin == 0 && range_end == 0
|
48
|
-
return ""
|
49
|
-
else
|
50
|
-
range_end -= 1
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
range_begin = @without_ansi.length - range.begin.abs if range.begin < 0
|
55
|
-
range_end = @without_ansi.length - range.end.abs if range.end < 0
|
56
|
-
|
57
|
-
str = build_string_with_ansi_for(range_begin..range_end)
|
58
|
-
ANSIString.new str if str
|
59
|
-
end
|
60
|
-
|
61
|
-
def []=(range, replacement_str)
|
62
|
-
# convert numeric position to a range
|
63
|
-
range = (range..range) if range.is_a?(Integer)
|
64
|
-
|
65
|
-
range_begin = range.begin
|
66
|
-
range_end = range.exclude_end? ? range.end - 1 : range.end
|
67
|
-
|
68
|
-
range_begin = @without_ansi.length - range.begin.abs if range.begin < 0
|
69
|
-
range_end = @without_ansi.length - range.end.abs if range.end < 0
|
70
|
-
|
71
|
-
updated_string = replace_in_string(range_begin..range_end, replacement_str)
|
72
|
-
process_string raw_string_for(updated_string)
|
73
|
-
self
|
74
|
-
end
|
75
|
-
|
76
|
-
# See String#rindex for arguments
|
77
|
-
def rindex(*args)
|
78
|
-
@without_ansi.rindex(*args)
|
79
|
-
end
|
80
|
-
|
81
|
-
def replace(str)
|
82
|
-
process_string raw_string_for(str)
|
83
|
-
self
|
84
|
-
end
|
85
|
-
|
86
|
-
def reverse
|
87
|
-
str = @ansi_sequence_locations.reverse.map do |location|
|
88
|
-
[location[:start_ansi_sequence], location[:text].reverse, location[:end_ansi_sequence]].join
|
89
|
-
end.join
|
90
|
-
ANSIString.new str
|
91
|
-
end
|
92
|
-
|
93
|
-
def scan(pattern)
|
94
|
-
results = []
|
95
|
-
without_ansi.enum_for(:scan, pattern).each do
|
96
|
-
md = Regexp.last_match
|
97
|
-
if md.captures.any?
|
98
|
-
results << md.captures.map.with_index do |_, i|
|
99
|
-
# captures use 1-based indexing
|
100
|
-
self[md.begin(i+1)..md.end(i+1)-1]
|
101
|
-
end
|
102
|
-
else
|
103
|
-
results << self[md.begin(0)..md.end(0)-1]
|
104
|
-
end
|
105
|
-
end
|
106
|
-
results
|
107
|
-
end
|
108
|
-
|
109
|
-
def slice(index, length=nil)
|
110
|
-
return ANSIString.new("") if length == 0
|
111
|
-
range = nil
|
112
|
-
index = index.without_ansi if index.is_a?(ANSIString)
|
113
|
-
index = Regexp.new Regexp.escape(index) if index.is_a?(String)
|
114
|
-
if index.is_a?(Integer)
|
115
|
-
length ||= 1
|
116
|
-
range = (index..index+length-1)
|
117
|
-
elsif index.is_a?(Range)
|
118
|
-
range = index
|
119
|
-
elsif index.is_a?(Regexp)
|
120
|
-
md = @without_ansi.match(index)
|
121
|
-
capture_group_index = length || 0
|
122
|
-
if md
|
123
|
-
capture_group = md.offset(capture_group_index)
|
124
|
-
range = (capture_group.first..capture_group.last-1)
|
125
|
-
end
|
126
|
-
else
|
127
|
-
raise(ArgumentError, "Must pass in at least an index or a range.")
|
128
|
-
end
|
129
|
-
self[range] if range
|
130
|
-
end
|
131
|
-
|
132
|
-
def split(*args)
|
133
|
-
raw.split(*args).map { |s| ANSIString.new(s) }
|
134
|
-
end
|
135
|
-
|
136
|
-
def strip
|
137
|
-
ANSIString.new raw.strip
|
138
|
-
end
|
139
|
-
|
140
|
-
def length
|
141
|
-
@without_ansi.length
|
142
|
-
end
|
143
|
-
|
144
|
-
def lines
|
145
|
-
result = []
|
146
|
-
current_string = ""
|
147
|
-
@ansi_sequence_locations.map do |location|
|
148
|
-
if location[:text] == "\n"
|
149
|
-
result << ANSIString.new(current_string + "\n")
|
150
|
-
current_string = ""
|
151
|
-
next
|
152
|
-
end
|
153
|
-
|
154
|
-
location[:text].scan(/.*(?:\n|$)/).each_with_index do |line, i|
|
155
|
-
break if line == ""
|
156
|
-
|
157
|
-
if i == 0
|
158
|
-
current_string << [
|
159
|
-
location[:start_ansi_sequence],
|
160
|
-
line,
|
161
|
-
location[:end_ansi_sequence]
|
162
|
-
].join
|
163
|
-
else
|
164
|
-
result << ANSIString.new(current_string)
|
165
|
-
current_string = ""
|
166
|
-
current_string << [
|
167
|
-
location[:start_ansi_sequence],
|
168
|
-
line,
|
169
|
-
location[:end_ansi_sequence]
|
170
|
-
].join
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
if location[:text].end_with?("\n")
|
175
|
-
result << ANSIString.new(current_string)
|
176
|
-
current_string = ""
|
177
|
-
next
|
178
|
-
end
|
179
|
-
end
|
180
|
-
result << ANSIString.new(current_string) if current_string.length > 0
|
181
|
-
result
|
182
|
-
end
|
183
|
-
|
184
|
-
def dup
|
185
|
-
ANSIString.new(@raw.dup)
|
186
|
-
end
|
187
|
-
|
188
|
-
def sub(pattern, replacement)
|
189
|
-
str = ""
|
190
|
-
count = 0
|
191
|
-
max_count = 1
|
192
|
-
index = 0
|
193
|
-
@without_ansi.enum_for(:scan, pattern).each do
|
194
|
-
md = Regexp.last_match
|
195
|
-
str << build_string_with_ansi_for(index...(index + md.begin(0)))
|
196
|
-
index = md.end(0)
|
197
|
-
break if (count += 1) == max_count
|
198
|
-
end
|
199
|
-
if index != @without_ansi.length
|
200
|
-
str << build_string_with_ansi_for(index..@without_ansi.length)
|
201
|
-
end
|
202
|
-
nstr = str.gsub /(\033\[[0-9;]*m)(.+?)\033\[0m\1/, '\1\2'
|
203
|
-
ANSIString.new(nstr)
|
204
|
-
end
|
205
|
-
|
206
|
-
def to_s
|
207
|
-
@raw.dup
|
208
|
-
end
|
209
|
-
alias :to_str :to_s
|
210
|
-
|
211
|
-
def inspect
|
212
|
-
to_s.inspect
|
213
|
-
end
|
214
|
-
|
215
|
-
def ==(other)
|
216
|
-
(other.class == self.class && other.raw == @raw) || (other.kind_of?(String) && other == @raw)
|
217
|
-
end
|
218
|
-
|
219
|
-
def <=>(other)
|
220
|
-
(other.class == self.class && @raw <=> other.raw)
|
221
|
-
end
|
222
|
-
|
223
|
-
private
|
224
|
-
|
225
|
-
def raw_string_for(str)
|
226
|
-
str.is_a?(ANSIString) ? str.raw : str.to_s
|
227
|
-
end
|
228
|
-
|
229
|
-
def process_string(raw_str)
|
230
|
-
@without_ansi = ""
|
231
|
-
@ansi_sequence_locations = []
|
232
|
-
raw_str.enum_for(:scan, /(\e\[[0-9;]*m)?(.*?)(?=\e\[[0-9;]*m|\Z)/m ).each do
|
233
|
-
md = Regexp.last_match
|
234
|
-
ansi_sequence, text = md.captures
|
235
|
-
|
236
|
-
previous_sequence_location = @ansi_sequence_locations.last
|
237
|
-
if previous_sequence_location
|
238
|
-
if ansi_sequence == "\e[0m"
|
239
|
-
previous_sequence_location[:end_ansi_sequence] = ansi_sequence
|
240
|
-
ansi_sequence = nil
|
241
|
-
elsif previous_sequence_location[:start_ansi_sequence] == ansi_sequence
|
242
|
-
previous_sequence_location[:text] << text
|
243
|
-
previous_sequence_location[:ends_at] += text.length
|
244
|
-
previous_sequence_location[:length] += text.length
|
245
|
-
@without_ansi << text
|
246
|
-
next
|
247
|
-
end
|
248
|
-
end
|
249
|
-
|
250
|
-
if ansi_sequence.nil? && text.to_s.length == 0
|
251
|
-
next
|
252
|
-
end
|
253
|
-
|
254
|
-
@ansi_sequence_locations.push(
|
255
|
-
begins_at: @without_ansi.length,
|
256
|
-
ends_at: [@without_ansi.length + text.length - 1, 0].max,
|
257
|
-
length: text.length,
|
258
|
-
text: text,
|
259
|
-
start_ansi_sequence: ansi_sequence
|
260
|
-
)
|
261
|
-
|
262
|
-
@without_ansi << text
|
263
|
-
end
|
264
|
-
|
265
|
-
@raw = @ansi_sequence_locations.map do |location|
|
266
|
-
[location[:start_ansi_sequence], location[:text], location[:end_ansi_sequence]].compact.join
|
267
|
-
end.join
|
268
|
-
|
269
|
-
@ansi_sequence_locations
|
270
|
-
end
|
271
|
-
|
272
|
-
def replace_in_string(range, replacement_str)
|
273
|
-
raise RangeError, "#{range.inspect} out of range" if range.begin > length
|
274
|
-
return replacement_str if @ansi_sequence_locations.empty?
|
275
|
-
|
276
|
-
range = range.begin..(range.end - 1) if range.exclude_end?
|
277
|
-
str = ""
|
278
|
-
@ansi_sequence_locations.each_with_index do |location, j|
|
279
|
-
# If the given range encompasses part of the location, then we want to
|
280
|
-
# include the whole location
|
281
|
-
if location[:begins_at] >= range.begin && location[:ends_at] <= range.end
|
282
|
-
end_index = range.end - location[:begins_at] + 1
|
283
|
-
|
284
|
-
str << [
|
285
|
-
location[:start_ansi_sequence],
|
286
|
-
replacement_str,
|
287
|
-
location[:text][end_index..-1],
|
288
|
-
location[:end_ansi_sequence]
|
289
|
-
].join
|
290
|
-
|
291
|
-
# If the location falls within the given range then make sure we pull
|
292
|
-
# out the bits that we want, and keep ANSI escape sequenece intact while
|
293
|
-
# doing so.
|
294
|
-
elsif location[:begins_at] <= range.begin && location[:ends_at] >= range.end
|
295
|
-
start_index = range.begin - location[:begins_at]
|
296
|
-
end_index = range.end - location[:begins_at] + 1
|
297
|
-
|
298
|
-
str << [
|
299
|
-
location[:start_ansi_sequence],
|
300
|
-
location[:text][0...start_index],
|
301
|
-
replacement_str,
|
302
|
-
location[:text][end_index..-1],
|
303
|
-
location[:end_ansi_sequence]
|
304
|
-
].join
|
305
|
-
|
306
|
-
elsif location[:ends_at] == range.begin
|
307
|
-
start_index = range.begin - location[:begins_at]
|
308
|
-
end_index = range.end
|
309
|
-
num_chars_to_remove_from_next_location = range.end - location[:ends_at]
|
310
|
-
|
311
|
-
str << [
|
312
|
-
location[:start_ansi_sequence],
|
313
|
-
location[:text][location[:begins_at]...(location[:begins_at]+start_index)],
|
314
|
-
replacement_str,
|
315
|
-
location[:text][end_index..-1],
|
316
|
-
location[:end_ansi_sequence],
|
317
|
-
].join
|
318
|
-
|
319
|
-
if location=@ansi_sequence_locations[j+1]
|
320
|
-
old = location.dup
|
321
|
-
location[:text][0...num_chars_to_remove_from_next_location] = ""
|
322
|
-
location[:begins_at] += num_chars_to_remove_from_next_location
|
323
|
-
location[:ends_at] += num_chars_to_remove_from_next_location
|
324
|
-
end
|
325
|
-
|
326
|
-
# If we're pushing onto the end of the string
|
327
|
-
elsif range.begin == length && location[:ends_at] == length - 1
|
328
|
-
if replacement_str.is_a?(ANSIString)
|
329
|
-
str << [location[:start_ansi_sequence], location[:text], location[:end_ansi_sequence], replacement_str].join
|
330
|
-
else
|
331
|
-
str << [location[:start_ansi_sequence], location[:text], replacement_str, location[:end_ansi_sequence]].join
|
332
|
-
end
|
333
|
-
else
|
334
|
-
str << [location[:start_ansi_sequence], location[:text], location[:end_ansi_sequence]].join
|
335
|
-
end
|
336
|
-
end
|
337
|
-
|
338
|
-
str
|
339
|
-
end
|
340
|
-
|
341
|
-
def build_string_with_ansi_for(range)
|
342
|
-
return nil if range.begin > length
|
343
|
-
|
344
|
-
str = ""
|
345
|
-
|
346
|
-
if range.exclude_end?
|
347
|
-
range = range.begin..(range.end - 1)
|
348
|
-
end
|
349
|
-
|
350
|
-
@ansi_sequence_locations.each do |location|
|
351
|
-
# If the given range encompasses part of the location, then we want to
|
352
|
-
# include the whole location
|
353
|
-
if location[:begins_at] >= range.begin && location[:ends_at] <= range.end
|
354
|
-
str << [location[:start_ansi_sequence], location[:text], location[:end_ansi_sequence]].join
|
355
|
-
|
356
|
-
elsif location[:begins_at] >= range.begin && location[:begins_at] <= range.end
|
357
|
-
str << [location[:start_ansi_sequence], location[:text][0..(range.end - location[:begins_at])], location[:end_ansi_sequence]].join
|
358
|
-
|
359
|
-
# If the location falls within the given range then make sure we pull
|
360
|
-
# out the bits that we want, and keep ANSI escape sequenece intact while
|
361
|
-
# doing so.
|
362
|
-
elsif (location[:begins_at] <= range.begin && location[:ends_at] >= range.end) || range.cover?(location[:ends_at])
|
363
|
-
start_index = range.begin - location[:begins_at]
|
364
|
-
end_index = range.end - location[:begins_at]
|
365
|
-
str << [location[:start_ansi_sequence], location[:text][start_index..end_index], location[:end_ansi_sequence]].join
|
366
|
-
end
|
367
|
-
end
|
368
|
-
str
|
369
|
-
end
|
370
|
-
|
371
|
-
end
|
data/spec/ansi_string_spec.rb
DELETED
@@ -1,701 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'term/ansicolor'
|
3
|
-
|
4
|
-
describe 'ANSIString' do
|
5
|
-
include Term::ANSIColor
|
6
|
-
|
7
|
-
describe "constructing" do
|
8
|
-
it "can be constructed with a String" do
|
9
|
-
ansi_string = ANSIString.new "this is a string"
|
10
|
-
expect(ansi_string).to be
|
11
|
-
end
|
12
|
-
|
13
|
-
it "can be constructed with a String containing ANSI escape sequences" do
|
14
|
-
ansi_string = ANSIString.new "this #{blue('is')} a string"
|
15
|
-
expect(ansi_string).to be
|
16
|
-
end
|
17
|
-
|
18
|
-
it "can be constructed with UTF-8 characters" do;
|
19
|
-
expect do
|
20
|
-
ansi_string = ANSIString.new "this #{blue('ƒ')} a string"
|
21
|
-
expect(ansi_string).to be
|
22
|
-
end.to_not raise_error
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
describe "redundant ANSI sequences" do
|
27
|
-
it "strips out redundant ANSI sequences that are immediately next to each other" do
|
28
|
-
ansi_string = ANSIString.new "this is\e[31m\e[31m a string"
|
29
|
-
expect(ansi_string.to_s).to eq "this is\e[31m a string"
|
30
|
-
end
|
31
|
-
|
32
|
-
it "strips out redundant ANSI sequences that are not immediately next to each other" do
|
33
|
-
ansi_string = ANSIString.new "this \e[31m a\e[31m string"
|
34
|
-
expect(ansi_string.to_s).to eq "this \e[31m a string"
|
35
|
-
end
|
36
|
-
|
37
|
-
it "does not strip out ANSI sequences that differ" do
|
38
|
-
ansi_string = ANSIString.new "this \e[31m a\e[32m string"
|
39
|
-
expect(ansi_string.to_s).to eq "this \e[31m a\e[32m string"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
describe "#+ combining strings" do
|
44
|
-
let(:blue_ansi_string){ ANSIString.new blue_string }
|
45
|
-
let(:yellow_ansi_string){ ANSIString.new yellow_string }
|
46
|
-
let(:blue_string){ blue("this is blue") }
|
47
|
-
let(:yellow_string){ yellow("this is yellow") }
|
48
|
-
|
49
|
-
it "returns a new string when combining two ANSIStrings" do
|
50
|
-
expect(blue_ansi_string + yellow_ansi_string).to eq ANSIString.new(blue_string + yellow_string)
|
51
|
-
end
|
52
|
-
|
53
|
-
it "returns a new string when combining a ANIString with a String" do
|
54
|
-
expect(blue_ansi_string + yellow_string).to eq ANSIString.new(blue_string + yellow_string)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
describe "#each_byte" do
|
59
|
-
let(:blue_ansi_string){ ANSIString.new blue_string }
|
60
|
-
let(:blue_string){ blue("this is blue") }
|
61
|
-
|
62
|
-
it "iterates over each character ignoring ANSI sequences" do
|
63
|
-
expected = "this is blue"
|
64
|
-
actual = ""
|
65
|
-
blue_ansi_string.each_byte { |ch| actual << ch }
|
66
|
-
expect(actual).to eq(expected)
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
describe "#each_char" do
|
71
|
-
let(:blue_ansi_string){ ANSIString.new blue_string }
|
72
|
-
let(:blue_string){ blue("this is blue") }
|
73
|
-
|
74
|
-
it "iterates over each character ignoring ANSI sequences" do
|
75
|
-
expected = "this is blue"
|
76
|
-
actual = ""
|
77
|
-
blue_ansi_string.each_char { |ch| actual << ch }
|
78
|
-
expect(actual).to eq(expected)
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
describe "#<<" do
|
83
|
-
it "appends a String onto the end of the current ANSIString" do
|
84
|
-
ansi_string = ANSIString.new ""
|
85
|
-
ansi_string << "a"
|
86
|
-
expect(ansi_string).to eq ANSIString.new("a")
|
87
|
-
|
88
|
-
ansi_string << "b"
|
89
|
-
expect(ansi_string).to eq ANSIString.new("ab")
|
90
|
-
|
91
|
-
ansi_string << "cd"
|
92
|
-
expect(ansi_string).to eq ANSIString.new("abcd")
|
93
|
-
end
|
94
|
-
|
95
|
-
it "appends an ANSIString onto the end of the current ANSIString" do
|
96
|
-
ansi_string = ANSIString.new ""
|
97
|
-
ansi_string << ANSIString.new(blue("a"))
|
98
|
-
expect(ansi_string).to eq ANSIString.new("#{blue('a')}")
|
99
|
-
|
100
|
-
ansi_string << ANSIString.new(yellow("b"))
|
101
|
-
expect(ansi_string).to eq ANSIString.new("#{blue('a')}#{yellow('b')}")
|
102
|
-
|
103
|
-
ansi_string << ANSIString.new(red("cd"))
|
104
|
-
expect(ansi_string).to eq ANSIString.new("#{blue('a')}#{yellow('b')}#{red('cd')}")
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
describe "#insert (see Ruby's String#insert for intent)" do
|
109
|
-
it "insert a string into the ANSIString" do
|
110
|
-
ansi_string = ANSIString.new "az"
|
111
|
-
ansi_string.insert 1, "thru"
|
112
|
-
expect(ansi_string).to eq ANSIString.new("athruz")
|
113
|
-
|
114
|
-
ansi_string.insert 0, "_"
|
115
|
-
expect(ansi_string).to eq ANSIString.new("_athruz")
|
116
|
-
|
117
|
-
ansi_string.insert ansi_string.length, "_"
|
118
|
-
expect(ansi_string).to eq ANSIString.new("_athruz_")
|
119
|
-
end
|
120
|
-
|
121
|
-
it "insert an ANSIString into an ANSIString" do
|
122
|
-
ansi_string = ANSIString.new blue("az")
|
123
|
-
ansi_string.insert 1, yellow("thru")
|
124
|
-
expect(ansi_string).to eq ANSIString.new("\e[34ma\e[33mthru\e[0mz\e[0m")
|
125
|
-
end
|
126
|
-
|
127
|
-
it "inserts from the end with a negative position" do
|
128
|
-
ansi_string = ANSIString.new blue("az")
|
129
|
-
ansi_string.insert -2, yellow("thru")
|
130
|
-
expect(ansi_string).to eq ANSIString.new("\e[34ma\e[33mthru\e[0mz\e[0m")
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
describe "#length" do
|
135
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
136
|
-
let(:string){ "this is blue" }
|
137
|
-
|
138
|
-
it "returns the length string without ANSI escape sequences" do
|
139
|
-
expect(ansi_string.length).to eq string.length
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
describe "#empty?" do
|
144
|
-
it "returns true when empty" do
|
145
|
-
expect(ANSIString.new("").empty?).to be(true)
|
146
|
-
end
|
147
|
-
|
148
|
-
it "returns true when it only contains ANSI sequences" do
|
149
|
-
expect(ANSIString.new(blue("")).empty?).to be(true)
|
150
|
-
end
|
151
|
-
|
152
|
-
it "returns false when there are non-ANSI characters" do
|
153
|
-
expect(ANSIString.new("a").empty?).to be(false)
|
154
|
-
expect(ANSIString.new(blue("a")).empty?).to be(false)
|
155
|
-
end
|
156
|
-
|
157
|
-
end
|
158
|
-
|
159
|
-
describe "#index" do
|
160
|
-
it "returns the index of the first occurrence of the given substring" do
|
161
|
-
ansi_string = ANSIString.new("this is not blue")
|
162
|
-
expect(ansi_string.index("b")).to eq 12
|
163
|
-
|
164
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
165
|
-
expect(ansi_string.index("blu")).to eq 8
|
166
|
-
|
167
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
168
|
-
expect(ansi_string.index("yellow")).to eq 25
|
169
|
-
end
|
170
|
-
|
171
|
-
it "returns the index starting on or after an optional start position" do
|
172
|
-
ansi_string = ANSIString.new("this is not blue")
|
173
|
-
expect(ansi_string.index("t", 0)).to eq 0
|
174
|
-
|
175
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
176
|
-
expect(ansi_string.index("is", 3)).to eq 5
|
177
|
-
expect(ansi_string.index("bl", 7)).to eq 8
|
178
|
-
expect(ansi_string.index("bl", 9)).to eq nil
|
179
|
-
|
180
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
181
|
-
expect(ansi_string.index("yel", 5)).to eq 25
|
182
|
-
expect(ansi_string.index("yel", 25)).to eq 25
|
183
|
-
expect(ansi_string.index("yel", 26)).to eq nil
|
184
|
-
end
|
185
|
-
|
186
|
-
it "returns the index of the first occurrence of the given regular expression" do
|
187
|
-
ansi_string = ANSIString.new("this is not blue")
|
188
|
-
expect(ansi_string.index(/b/)).to eq 12
|
189
|
-
|
190
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
191
|
-
expect(ansi_string.index(/blu/)).to eq 8
|
192
|
-
|
193
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
194
|
-
expect(ansi_string.index(/y.ll.w/)).to eq 25
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
describe "#rindex" do
|
199
|
-
it "returns the index of the last occurrence of the given substring" do
|
200
|
-
ansi_string = ANSIString.new("this is not blue")
|
201
|
-
expect(ansi_string.rindex("i")).to eq 5
|
202
|
-
|
203
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
204
|
-
expect(ansi_string.rindex("blu")).to eq 8
|
205
|
-
|
206
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
207
|
-
expect(ansi_string.rindex("yellow")).to eq 25
|
208
|
-
end
|
209
|
-
|
210
|
-
it "returns the index of the match on or after an optional stop position" do
|
211
|
-
ansi_string = ANSIString.new("this is not blue")
|
212
|
-
expect(ansi_string.rindex("t", 0)).to eq 0
|
213
|
-
expect(ansi_string.rindex("is", 3)).to eq 2
|
214
|
-
expect(ansi_string.rindex("bl", 12)).to eq 12
|
215
|
-
|
216
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
217
|
-
expect(ansi_string.rindex("is", 0)).to eq nil
|
218
|
-
expect(ansi_string.rindex("is", 3)).to eq 2
|
219
|
-
expect(ansi_string.rindex("bl", 8)).to eq 8
|
220
|
-
expect(ansi_string.rindex("bl", 12)).to eq 8
|
221
|
-
|
222
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
223
|
-
expect(ansi_string.rindex("yel", 5)).to eq nil
|
224
|
-
expect(ansi_string.rindex("yel", 25)).to eq 25
|
225
|
-
expect(ansi_string.rindex("yel", 26)).to eq 25
|
226
|
-
end
|
227
|
-
|
228
|
-
it "returns the index of the last occurrence of the given regular expression" do
|
229
|
-
ansi_string = ANSIString.new("this is not blue")
|
230
|
-
expect(ansi_string.rindex(/b/)).to eq 12
|
231
|
-
|
232
|
-
ansi_string = ANSIString.new("this is #{blue('blue')}")
|
233
|
-
expect(ansi_string.rindex(/blu/)).to eq 8
|
234
|
-
|
235
|
-
ansi_string = ANSIString.new("this is #{blue('blue')} and this is #{yellow('yellow')}")
|
236
|
-
expect(ansi_string.rindex(/y.ll.w/)).to eq 25
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
describe "#[]" do
|
241
|
-
subject(:ansi_string){ ANSIString.new "#{blue_string}ABC#{yellow_string}" }
|
242
|
-
let(:blue_string){ blue("this is blue") }
|
243
|
-
let(:yellow_string){ yellow("this is yellow") }
|
244
|
-
|
245
|
-
it "returns the full substring with the appropriate ANSI start and end sequence" do
|
246
|
-
expect(ansi_string[0...12]).to eq ANSIString.new(blue("this is blue"))
|
247
|
-
expect(ansi_string[15..-1]).to eq ANSIString.new(yellow("this is yellow"))
|
248
|
-
end
|
249
|
-
|
250
|
-
it "returns a partial substring with the appropriate ANSI start sequence and provides an end sequence" do
|
251
|
-
expect(ansi_string[0..1]).to eq blue("th")
|
252
|
-
expect(ansi_string[17..-5]).to eq yellow("is is ye")
|
253
|
-
end
|
254
|
-
|
255
|
-
it "returns the correct substring when location of an ANSI sequence comes before the end of the request" do
|
256
|
-
s = ANSIString.new("ABC \e[7mGemfile.lock\e[0m LICENSE.txt README.md")
|
257
|
-
expect(s[4...28]).to eq ANSIString.new("\e[7mGemfile.lock\e[0m LICENSE.txt")
|
258
|
-
end
|
259
|
-
|
260
|
-
it "returns text that is not ANSI escaped" do
|
261
|
-
expect(ansi_string[12..14]).to eq "ABC"
|
262
|
-
end
|
263
|
-
|
264
|
-
it "returns dos thine" do
|
265
|
-
ansi_string = ANSIString.new("ABC")
|
266
|
-
expect(ansi_string[0...1]).to eq "A"
|
267
|
-
end
|
268
|
-
|
269
|
-
it "returns up to the end" do
|
270
|
-
expect(ansi_string[-2..-1]).to eq yellow("ow")
|
271
|
-
end
|
272
|
-
|
273
|
-
context "and the range is around the ANSI sequence location in the string" do
|
274
|
-
it "returns the string with the ANSI sequences within it intact" do
|
275
|
-
ansi_string = ANSIString.new "abc#{green('def')}ghi"
|
276
|
-
expect(ansi_string[0..-1]).to eq "abc#{green('def')}ghi"
|
277
|
-
end
|
278
|
-
|
279
|
-
it "returns the string with the ANSI sequences within it intact" do
|
280
|
-
ansi_string = ANSIString.new "abc#{green('def')}ghi"
|
281
|
-
expect(ansi_string[0..2]).to eq "abc"
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
it "returns nil when the given range is beyond the length of the string" do
|
286
|
-
ansi_string = ANSIString.new "abc"
|
287
|
-
expect(ansi_string[4]).to be nil
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
describe "#[]=" do
|
292
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
293
|
-
let(:string){ "this is blue" }
|
294
|
-
|
295
|
-
it "returns a new ANSIString with the string at the given index replaced with the new string" do
|
296
|
-
ansi_string[1] = "Z"
|
297
|
-
expect(ansi_string).to eq ANSIString.new(blue("tZis is blue"))
|
298
|
-
end
|
299
|
-
|
300
|
-
it "returns a new ANSIString with the string at the given range replaced with the new string" do
|
301
|
-
ansi_string[1..2] = "ZYX"
|
302
|
-
expect(ansi_string).to eq ANSIString.new(blue("tZYXs is blue"))
|
303
|
-
end
|
304
|
-
|
305
|
-
it "supports replacing with negative indexes at the front of the string" do
|
306
|
-
ansi_string[0..-1] = "abc"
|
307
|
-
expect(ansi_string).to eq ANSIString.new(blue("abc"))
|
308
|
-
end
|
309
|
-
|
310
|
-
it "supports replacing with negative indexes in the middle of the string" do
|
311
|
-
ansi_string = ANSIString.new(blue("abc"))
|
312
|
-
ansi_string[1..-2] = red("*")
|
313
|
-
|
314
|
-
# Do not preserve reset sequences (e.g. "\e[0m") when inserting/replacing.
|
315
|
-
# So no \e[0m before the replacement '*'
|
316
|
-
expect(ansi_string).to eq ANSIString.new("\e[34ma\e[31m*\e[0mc\e[0m")
|
317
|
-
end
|
318
|
-
|
319
|
-
it "preserves coloring when part of the text with a String" do
|
320
|
-
ansi_string[0..3] = "that"
|
321
|
-
expect(ansi_string).to eq ANSIString.new(blue("that is blue"))
|
322
|
-
end
|
323
|
-
|
324
|
-
it "preserves coloring when replacing all of the text with a String" do
|
325
|
-
ansi_string[0..11] = "foobar"
|
326
|
-
expect(ansi_string).to eq ANSIString.new(blue("foobar"))
|
327
|
-
end
|
328
|
-
|
329
|
-
it "preserves coloring when part of the text with a String and we're not starting at an index of 0" do
|
330
|
-
ansi_string[5..6] = "ain't"
|
331
|
-
expect(ansi_string).to eq ANSIString.new(blue("this ain't blue"))
|
332
|
-
end
|
333
|
-
|
334
|
-
context "appending a string to the very end" do
|
335
|
-
subject(:ansi_string){ ANSIString.new green("CircleCI pass") }
|
336
|
-
|
337
|
-
it "combines when the ANSI sequences are the same" do
|
338
|
-
ansi_string[13..15] = ANSIString.new green("ed")
|
339
|
-
expect(ansi_string).to eq ANSIString.new(green("CircleCI passed"))
|
340
|
-
end
|
341
|
-
|
342
|
-
it "doesn't combine when the ANSI sequences are different" do
|
343
|
-
ansi_string[13..15] = ANSIString.new red("ed")
|
344
|
-
expect(ansi_string).to eq ANSIString.new(green("CircleCI pass") + red("ed"))
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
context "replacing on newline boundaries" do
|
349
|
-
subject(:ansi_string){ ANSIString.new "this\nthat" }
|
350
|
-
|
351
|
-
it "keeps the new line intact" do
|
352
|
-
ansi_string[2...4] = "IS"
|
353
|
-
expect(ansi_string).to eq ANSIString.new("thIS\nthat")
|
354
|
-
end
|
355
|
-
end
|
356
|
-
|
357
|
-
context "replacing the same location twice" do
|
358
|
-
subject(:ansi_string){ ANSIString.new "this\nthat" }
|
359
|
-
|
360
|
-
it "keeps the new line intact" do
|
361
|
-
ansi_string[2...4] = blue("IS")
|
362
|
-
ansi_string[2...4] = blue("IS")
|
363
|
-
expect(ansi_string).to eq ANSIString.new("th#{blue('IS')}\nthat")
|
364
|
-
end
|
365
|
-
end
|
366
|
-
|
367
|
-
context "replacing a substring that goes across ANSI sequence boundaries" do
|
368
|
-
subject(:ansi_string){ ANSIString.new "this#{blue('that')}" }
|
369
|
-
|
370
|
-
it "moves the boundaries when using positive indexes and a regular String replacement" do
|
371
|
-
ansi_string[3..4] = yellow("SORRY")
|
372
|
-
expect(ansi_string).to eq ANSIString.new("thi#{yellow('SORRY')}#{blue('hat')}")
|
373
|
-
end
|
374
|
-
|
375
|
-
it "moves the boundaries when using positive indexes and an ANSIString replacement" do
|
376
|
-
ansi_string[3..4] = yellow("SORRY")
|
377
|
-
expect(ansi_string).to eq ANSIString.new("thi#{yellow('SORRY')}#{blue('hat')}")
|
378
|
-
end
|
379
|
-
|
380
|
-
it "moves the boundaries when using negatives indexes and a regular String replacement" do
|
381
|
-
ansi_string[-5..4] = "SORRY"
|
382
|
-
expect(ansi_string).to eq ANSIString.new("thiSORRY#{blue('hat')}")
|
383
|
-
end
|
384
|
-
|
385
|
-
it "moves the boundaries when using negatives indexes and an ANSIString replacement" do
|
386
|
-
ansi_string[-5..4] = yellow("SORRY")
|
387
|
-
expect(ansi_string).to eq ANSIString.new("thi#{yellow('SORRY')}#{blue('hat')}")
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
context "clearing the string" do
|
392
|
-
subject(:ansi_string){ ANSIString.new "this\nthat" }
|
393
|
-
|
394
|
-
it "clears the string" do
|
395
|
-
ansi_string[0..-1] = ""
|
396
|
-
expect(ansi_string).to eq ANSIString.new("")
|
397
|
-
end
|
398
|
-
end
|
399
|
-
|
400
|
-
context "expanding a string" do
|
401
|
-
subject(:ansi_string){ ANSIString.new "" }
|
402
|
-
|
403
|
-
it "expands the string" do
|
404
|
-
ansi_string[0..-1] = ANSIString.new(blue("HI"))
|
405
|
-
expect(ansi_string).to eq ANSIString.new(blue("HI"))
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
it "raises an error out of index" do
|
410
|
-
expect {
|
411
|
-
ansi_string[14..15] = string
|
412
|
-
}.to raise_error(RangeError, "14..15 out of range")
|
413
|
-
end
|
414
|
-
|
415
|
-
context "replacing a substring that comes entirely after an ANSI sequence" do
|
416
|
-
subject(:ansi_string){ ANSIString.new "this #{blue('is')} your television screen." }
|
417
|
-
|
418
|
-
it "places the substring in the correct location" do
|
419
|
-
ansi_string[14..15] = "YO YO"
|
420
|
-
expect(ansi_string).to eq ANSIString.new "this #{blue('is')} your tYO YOevision screen."
|
421
|
-
end
|
422
|
-
end
|
423
|
-
end
|
424
|
-
|
425
|
-
describe "#dup" do
|
426
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
427
|
-
let(:string){ "this is blue" }
|
428
|
-
|
429
|
-
it "returns a dup'd version of itself" do
|
430
|
-
duped = ansi_string.dup
|
431
|
-
expect(duped).to be_kind_of(ANSIString)
|
432
|
-
expect(duped.raw).to eq(ansi_string.raw)
|
433
|
-
end
|
434
|
-
end
|
435
|
-
|
436
|
-
describe "#lines" do
|
437
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
438
|
-
let(:string){ "this\nis\nblue" }
|
439
|
-
|
440
|
-
it "returns lines" do
|
441
|
-
expect(ansi_string.lines).to eq [
|
442
|
-
ANSIString.new(blue("this\n")),
|
443
|
-
ANSIString.new(blue("is\n")),
|
444
|
-
ANSIString.new(blue("blue"))
|
445
|
-
]
|
446
|
-
end
|
447
|
-
|
448
|
-
it "returns lines" do
|
449
|
-
ansi_string = ANSIString.new blue("abc") + "\n" + red("d\nef") + "hi\n" + yellow("foo")
|
450
|
-
expect(ansi_string.lines).to eq [
|
451
|
-
ANSIString.new(blue("abc") + "\n"),
|
452
|
-
ANSIString.new(red("d\n")),
|
453
|
-
ANSIString.new(red("ef") + "hi\n"),
|
454
|
-
ANSIString.new(yellow("foo"))
|
455
|
-
]
|
456
|
-
end
|
457
|
-
end
|
458
|
-
|
459
|
-
describe "#==" do
|
460
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
461
|
-
let(:string){ "this is blue" }
|
462
|
-
|
463
|
-
it "returns true when comparing against itself" do
|
464
|
-
expect(ansi_string).to eq ansi_string
|
465
|
-
end
|
466
|
-
|
467
|
-
it "returns true when comparing against another ANSIString with the same contents" do
|
468
|
-
expect(ansi_string).to eq ANSIString.new(blue(string))
|
469
|
-
end
|
470
|
-
|
471
|
-
it "returns false when comparing against another ANSIString with differnent contents" do
|
472
|
-
expect(ansi_string).to_not eq ANSIString.new(blue("other stuff"))
|
473
|
-
end
|
474
|
-
|
475
|
-
it "returns true when comparing against a String with the same raw contents" do
|
476
|
-
expect(ansi_string).to eq blue(string)
|
477
|
-
end
|
478
|
-
|
479
|
-
it "returns true when comparing against a String that doesn't match its raw contents" do
|
480
|
-
expect(ansi_string).to_not eq "asfsd"
|
481
|
-
end
|
482
|
-
end
|
483
|
-
|
484
|
-
describe "<=>" do
|
485
|
-
let(:string_1){ ANSIString.new blue("abc") }
|
486
|
-
let(:string_2){ ANSIString.new blue("def") }
|
487
|
-
|
488
|
-
it "behaves the same as a normal string" do
|
489
|
-
expect(string_1 <=> string_2).to eq(-1)
|
490
|
-
expect(string_1 <=> string_1).to eq(0)
|
491
|
-
expect(string_2 <=> string_1).to eq(1)
|
492
|
-
end
|
493
|
-
end
|
494
|
-
|
495
|
-
describe "#match" do
|
496
|
-
it "matches on a string pattren" do
|
497
|
-
string = "apples are bananas are they not?"
|
498
|
-
ansi_string = ANSIString.new("app#{red('les are bananas')} are they not?")
|
499
|
-
expect(ansi_string.match("are")).to eq(string.match("are"))
|
500
|
-
end
|
501
|
-
|
502
|
-
it "matches on a regex pattren" do
|
503
|
-
string = "apples are bananas are they not?"
|
504
|
-
ansi_string = ANSIString.new("app#{red('les are bananas')} are they not?")
|
505
|
-
expect(ansi_string.match(/are/)).to eq(string.match(/are/))
|
506
|
-
end
|
507
|
-
end
|
508
|
-
|
509
|
-
describe "#=~" do
|
510
|
-
it "matches on a regex pattren" do
|
511
|
-
string = "apples are bananas are they not?"
|
512
|
-
ansi_string = ANSIString.new("app#{red('les are bananas')} are they not?")
|
513
|
-
expect(ansi_string =~ /are/).to eq(string =~ /are/)
|
514
|
-
end
|
515
|
-
end
|
516
|
-
|
517
|
-
describe "#scan" do
|
518
|
-
it "scans without capture groups" do
|
519
|
-
string = "567"
|
520
|
-
ansi_string = ANSIString.new("1234#{red('5678')}90")
|
521
|
-
expect(ansi_string.scan(/.{2}/)).to eq([
|
522
|
-
ANSIString.new("12"),
|
523
|
-
ANSIString.new("34"),
|
524
|
-
ANSIString.new("#{red('56')}"),
|
525
|
-
ANSIString.new("#{red('78')}"),
|
526
|
-
ANSIString.new("90")
|
527
|
-
])
|
528
|
-
end
|
529
|
-
|
530
|
-
it "scans with capture groups" do
|
531
|
-
string = "567"
|
532
|
-
ansi_string = ANSIString.new("1234#{red('5678')}90")
|
533
|
-
expect(ansi_string.scan(/(.)./)).to eq([
|
534
|
-
[ANSIString.new("1")],
|
535
|
-
[ANSIString.new("3")],
|
536
|
-
[ANSIString.new("#{red('5')}")],
|
537
|
-
[ANSIString.new("#{red('7')}")],
|
538
|
-
[ANSIString.new("9")]
|
539
|
-
])
|
540
|
-
end
|
541
|
-
end
|
542
|
-
|
543
|
-
describe "#replace" do
|
544
|
-
it "replaces the contents of the current string with the new string" do
|
545
|
-
ansi_string = ANSIString.new("abc")
|
546
|
-
original_object_id = ansi_string.object_id
|
547
|
-
expect(ansi_string.replace("def")).to eq ANSIString.new("def")
|
548
|
-
expect(ansi_string.object_id).to eq(original_object_id)
|
549
|
-
end
|
550
|
-
end
|
551
|
-
|
552
|
-
describe "#reverse" do
|
553
|
-
it "reverses the string" do
|
554
|
-
ansi_string = ANSIString.new("abc")
|
555
|
-
expect(ansi_string.reverse).to eq ANSIString.new("cba")
|
556
|
-
end
|
557
|
-
|
558
|
-
it "reverses the string with ANSI sequences" do
|
559
|
-
ansi_string = ANSIString.new("a#{blue('b')}#{yellow('c')}")
|
560
|
-
expect(ansi_string.reverse).to eq ANSIString.new("#{yellow('c')}#{blue('b')}a")
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
describe "#slice" do
|
565
|
-
it "returns a substring of one character given a numeric index" do
|
566
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
567
|
-
expect(ansi_string.slice(0)).to eq ANSIString.new("a")
|
568
|
-
expect(ansi_string.slice(1)).to eq ANSIString.new(blue("b"))
|
569
|
-
expect(ansi_string.slice(2)).to eq ANSIString.new("c")
|
570
|
-
end
|
571
|
-
|
572
|
-
it "returns a substring of characters of N length given a start index and max length N" do
|
573
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
574
|
-
expect(ansi_string.slice(0, 0)).to eq ANSIString.new("")
|
575
|
-
expect(ansi_string.slice(0, 2)).to eq ANSIString.new("a#{blue('b')}")
|
576
|
-
expect(ansi_string.slice(1, 2)).to eq ANSIString.new("#{blue('b')}c")
|
577
|
-
|
578
|
-
# length is over, doesn't blow up
|
579
|
-
expect(ansi_string.slice(1, 3)).to eq ANSIString.new("#{blue('b')}c")
|
580
|
-
end
|
581
|
-
|
582
|
-
it "returns a substring of characters using a range as delimiters" do
|
583
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
584
|
-
expect(ansi_string.slice(0..1)).to eq ANSIString.new("a#{blue('b')}")
|
585
|
-
expect(ansi_string.slice(0...2)).to eq ANSIString.new("a#{blue('b')}")
|
586
|
-
|
587
|
-
# length is over, doesn't blow up
|
588
|
-
expect(ansi_string.slice(1..3)).to eq ANSIString.new("#{blue('b')}c")
|
589
|
-
end
|
590
|
-
|
591
|
-
it "returns a substring of characters matching the given regex" do
|
592
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
593
|
-
expect(ansi_string.slice(/b/)).to eq ANSIString.new("#{blue('b')}")
|
594
|
-
expect(ansi_string.slice(/(b)c/)).to eq ANSIString.new("#{blue('b')}c")
|
595
|
-
|
596
|
-
# length is over, doesn't blow up
|
597
|
-
expect(ansi_string.slice(/.*/)).to eq ANSIString.new("a#{blue('b')}c")
|
598
|
-
end
|
599
|
-
|
600
|
-
it "returns a substring for the capture group matching the given regex and capture group index" do
|
601
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
602
|
-
expect(ansi_string.slice(/((a)(b)(c))/, 1)).to eq ANSIString.new("a#{blue('b')}c")
|
603
|
-
expect(ansi_string.slice(/((a)(b)(c))/, 2)).to eq ANSIString.new("a")
|
604
|
-
expect(ansi_string.slice(/((a)(b)(c))/, 3)).to eq ANSIString.new("#{blue('b')}")
|
605
|
-
expect(ansi_string.slice(/((a)(b)(c))/, 4)).to eq ANSIString.new("c")
|
606
|
-
end
|
607
|
-
|
608
|
-
it "returns the substring when a given string is found" do
|
609
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
610
|
-
expect(ansi_string.slice("bc")).to eq(ANSIString.new("#{blue('b')}c"))
|
611
|
-
end
|
612
|
-
|
613
|
-
it "returns nil when no matches are found" do
|
614
|
-
ansi_string = ANSIString.new("a#{blue('b')}c")
|
615
|
-
expect(ansi_string.slice("zzz")).to be nil
|
616
|
-
expect(ansi_string.slice(/zzz/)).to be nil
|
617
|
-
expect(ansi_string.slice(99)).to be nil
|
618
|
-
expect(ansi_string.slice(99, 100)).to be nil
|
619
|
-
expect(ansi_string.slice(99..100)).to be nil
|
620
|
-
end
|
621
|
-
end
|
622
|
-
|
623
|
-
describe "#split" do
|
624
|
-
it "splits on the given string pattern" do
|
625
|
-
ansi_string = ANSIString.new("apples are #{red('red')}. bananas are #{blue('blue')}. cats are #{yellow('yellow')}.")
|
626
|
-
expect(ansi_string.split(". ")).to eq([
|
627
|
-
ANSIString.new("apples are #{red('red')}"),
|
628
|
-
ANSIString.new("bananas are #{blue('blue')}"),
|
629
|
-
ANSIString.new("cats are #{yellow('yellow')}.")
|
630
|
-
])
|
631
|
-
end
|
632
|
-
|
633
|
-
it "splits on the given regex pattern" do
|
634
|
-
ansi_string = ANSIString.new("apples are #{red('red')}. bananas are #{blue('blue')}. cats are #{yellow('yellow')}.")
|
635
|
-
expect(ansi_string.split(/\.\s?/)).to eq([
|
636
|
-
ANSIString.new("apples are #{red('red')}"),
|
637
|
-
ANSIString.new("bananas are #{blue('blue')}"),
|
638
|
-
ANSIString.new("cats are #{yellow('yellow')}")
|
639
|
-
])
|
640
|
-
end
|
641
|
-
|
642
|
-
it "limits how many times it splits with a secondary limit argument" do
|
643
|
-
ansi_string = ANSIString.new("apples are #{red('red')}. bananas are #{blue('blue')}. cats are #{yellow('yellow')}.")
|
644
|
-
expect(ansi_string.split(/\.\s?/, 2)).to eq([
|
645
|
-
ANSIString.new("apples are #{red('red')}"),
|
646
|
-
ANSIString.new("bananas are #{blue('blue')}. cats are #{yellow('yellow')}.")
|
647
|
-
])
|
648
|
-
end
|
649
|
-
end
|
650
|
-
|
651
|
-
describe "#strip" do
|
652
|
-
it 'returns a copy of the string with leading and trailing whitespace removed' do
|
653
|
-
ansi_string = ANSIString.new " this is his #{blue('pig')} "
|
654
|
-
expect(ansi_string.strip).to eq ANSIString.new "this is his #{blue('pig')}"
|
655
|
-
expect(ansi_string).to eq ANSIString.new " this is his #{blue('pig')} "
|
656
|
-
end
|
657
|
-
end
|
658
|
-
|
659
|
-
describe "#sub" do
|
660
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
661
|
-
let(:string){ "this is blue" }
|
662
|
-
|
663
|
-
it "returns an ANSIString" do
|
664
|
-
expect(ansi_string.sub(/ is /, "")).to eq ANSIString.new(blue("thisblue"))
|
665
|
-
end
|
666
|
-
|
667
|
-
it "works across ansi sequences" do
|
668
|
-
blue_string = blue("this is blue")
|
669
|
-
yellow_string = yellow("this is yellow")
|
670
|
-
non_colored_string = "hi there\nbye there"
|
671
|
-
str = ANSIString.new(blue_string + yellow_string + non_colored_string + " \n \n \n ")
|
672
|
-
expect(str.sub(/\s*\Z/m, "")).to eq ANSIString.new(blue_string + yellow_string + non_colored_string)
|
673
|
-
end
|
674
|
-
end
|
675
|
-
|
676
|
-
describe "#gsub" do
|
677
|
-
it "needs to be implemented"
|
678
|
-
end
|
679
|
-
|
680
|
-
describe "#to_s" do
|
681
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
682
|
-
let(:string){ "this is blue" }
|
683
|
-
|
684
|
-
it "returns the ANSI capable string" do
|
685
|
-
expect(ansi_string.to_s).to eq blue(string)
|
686
|
-
end
|
687
|
-
end
|
688
|
-
|
689
|
-
describe "#inspect" do
|
690
|
-
subject(:ansi_string){ ANSIString.new blue(string) }
|
691
|
-
let(:string){ "this is blue" }
|
692
|
-
|
693
|
-
it "returns a quoted version of the strong" do
|
694
|
-
expect(ansi_string.inspect).to eq "\"\\e[34mthis is blue\\e[0m\""
|
695
|
-
end
|
696
|
-
end
|
697
|
-
|
698
|
-
describe "#succ" do
|
699
|
-
it "needs to be implemented"
|
700
|
-
end
|
701
|
-
end
|