verse 0.3.0 → 0.4.0
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +27 -0
- data/lib/verse.rb +2 -0
- data/lib/verse/alignment.rb +11 -5
- data/lib/verse/padding.rb +4 -3
- data/lib/verse/sanitizer.rb +12 -0
- data/lib/verse/truncation.rb +23 -9
- data/lib/verse/version.rb +1 -1
- data/lib/verse/wrapping.rb +71 -14
- data/spec/unit/alignment/align_spec.rb +17 -0
- data/spec/unit/alignment/left_spec.rb +18 -1
- data/spec/unit/alignment/right_spec.rb +18 -1
- data/spec/unit/padding/pad_spec.rb +13 -0
- data/spec/unit/sanitizer/ansi_spec.rb +11 -0
- data/spec/unit/truncation/truncate_spec.rb +2 -2
- data/spec/unit/wrapping/wrap_spec.rb +57 -0
- data/verse.gemspec +1 -1
- metadata +5 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 91a9c0b0722656e5b2b160a14e68f9d228830120
|
4
|
+
data.tar.gz: 9397441752523d44ac885b7e5ad4b0e8faffbead
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa1fb93fb455d0d141616c0a0dd08886486331a92b5b165a3ec1852f228b89e8002fb078d9329b0c9325a758725004ebb14f74c5ba618145de9c29990b2b0519
|
7
|
+
data.tar.gz: bf4deb4d61427c35aab5d4522b31003a49e1b93388e176fd19533dfe3177e618126beee94893de210b14f64b5c51555b707cb7b39b1d17dd168ead985af54c6d
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,11 @@
|
|
1
|
+
0.4.0 (Mar 28, 2015)
|
2
|
+
|
3
|
+
* Add Sanitizer#ansi? to check for ANSI codes
|
4
|
+
* Change Alignment to work with ANSI codes
|
5
|
+
* Change Truncation to work with ANSI codes
|
6
|
+
* Change Wrapping to work with ANSI codes
|
7
|
+
* Chnage Padding to work with ANSI codes
|
8
|
+
|
1
9
|
0.3.0 (Feb 28, 2015)
|
2
10
|
|
3
11
|
* Add Sanitizer#replace for substitutiong linebreaks
|
data/README.md
CHANGED
@@ -19,6 +19,7 @@
|
|
19
19
|
* Simple API that can be easily wrapped by other objects
|
20
20
|
* Supports multibyte character encodings such as UTF-8, EUC-JP
|
21
21
|
* Handles languages without whitespaces between words (like Chinese and Japanese)
|
22
|
+
* Supports ANSI escape codes
|
22
23
|
|
23
24
|
## Installation
|
24
25
|
|
@@ -87,6 +88,14 @@ alignment.center(20) # =>
|
|
87
88
|
" 場にも含み "
|
88
89
|
```
|
89
90
|
|
91
|
+
**Verse::Alignment** works with ANSI escape codoes:
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
alignment = Verse::Alignment.new "\e[32mthe madness of men\e[0m"
|
95
|
+
alignment.align(22, :center)
|
96
|
+
# => " \e[32mthe madness of men\e[0m "
|
97
|
+
```
|
98
|
+
|
90
99
|
### 1.2 Pad
|
91
100
|
|
92
101
|
**Verse::Padding** provides facility to pad around text with a given padding.
|
@@ -178,6 +187,14 @@ truncation = Verse::Truncation.new 'ラドクリフ、マラソン五輪代表
|
|
178
187
|
truncation.truncate(12) # => "ラドクリフ…"
|
179
188
|
```
|
180
189
|
|
190
|
+
**Verse::Truncation** works with ANSI escape codoes:
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
truncation = Verse::Trucnation.new "I try \e[34mall things\e[0m, I achieve what I can"
|
194
|
+
truncation.truncate(18)
|
195
|
+
# => "I try \e[34mall things\e[0m…"
|
196
|
+
```
|
197
|
+
|
181
198
|
### 1.5 Wrap
|
182
199
|
|
183
200
|
**Verse::Wrapping** allows you to wrap text into lines no longer than `wrap_at` argument length. The `wrap` method will break either on whitespace character or in case of east Asian characters on character boundaries.
|
@@ -210,6 +227,16 @@ wrapping.wrap(8) # =>
|
|
210
227
|
"にも含み"
|
211
228
|
```
|
212
229
|
|
230
|
+
**Verse::Wrapping** knows how to handle ANSI codes:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
wrapping = Verse::Wrapping.new "\e[32;44mIgnorance is the parent of fear.\e[0m"
|
234
|
+
wrapping.wrap(14) # =>
|
235
|
+
"\e[32;44mIgnorance is \e[0m"
|
236
|
+
"\e[32;44mthe parent of \e[0m"
|
237
|
+
"\e[32;44mfear.\e[0m"
|
238
|
+
```
|
239
|
+
|
213
240
|
You can also call `wrap` directly on **Verse**:
|
214
241
|
|
215
242
|
```ruby
|
data/lib/verse.rb
CHANGED
data/lib/verse/alignment.rb
CHANGED
@@ -12,8 +12,9 @@ module Verse
|
|
12
12
|
#
|
13
13
|
# @api public
|
14
14
|
def initialize(text, options = {})
|
15
|
-
@text
|
16
|
-
@
|
15
|
+
@text = text
|
16
|
+
@sanitizer = Sanitizer.new
|
17
|
+
@fill = options.fetch(:fill) { SPACE }
|
17
18
|
@direction = options.fetch(:direction) { :left }
|
18
19
|
end
|
19
20
|
|
@@ -103,7 +104,7 @@ module Verse
|
|
103
104
|
|
104
105
|
# @api private
|
105
106
|
def left_justify(text, width, filler)
|
106
|
-
width_diff = width -
|
107
|
+
width_diff = width - actual_width(text)
|
107
108
|
if width_diff > 0
|
108
109
|
text + filler * width_diff
|
109
110
|
else
|
@@ -113,7 +114,7 @@ module Verse
|
|
113
114
|
|
114
115
|
# @api private
|
115
116
|
def right_justify(text, width, filler)
|
116
|
-
width_diff = width -
|
117
|
+
width_diff = width - actual_width(text)
|
117
118
|
if width_diff > 0
|
118
119
|
filler * width_diff + text
|
119
120
|
else
|
@@ -123,7 +124,7 @@ module Verse
|
|
123
124
|
|
124
125
|
# @api private
|
125
126
|
def center_justify(text, width, filler)
|
126
|
-
text_width =
|
127
|
+
text_width = actual_width(text)
|
127
128
|
width_diff = width - text_width
|
128
129
|
if width_diff > 0
|
129
130
|
right_count = (width_diff.to_f / 2).ceil
|
@@ -134,6 +135,11 @@ module Verse
|
|
134
135
|
end
|
135
136
|
end
|
136
137
|
|
138
|
+
# @api private
|
139
|
+
def actual_width(text)
|
140
|
+
UnicodeUtils.display_width(@sanitizer.sanitize(text))
|
141
|
+
end
|
142
|
+
|
137
143
|
attr_reader :text
|
138
144
|
end # Alignment
|
139
145
|
end # Verse
|
data/lib/verse/padding.rb
CHANGED
@@ -7,8 +7,9 @@ module Verse
|
|
7
7
|
#
|
8
8
|
# @api public
|
9
9
|
def initialize(text, options = {})
|
10
|
-
@text
|
11
|
-
@padding
|
10
|
+
@text = text
|
11
|
+
@padding = Padder.parse(options[:padding])
|
12
|
+
@sanitizer = Sanitizer.new
|
12
13
|
end
|
13
14
|
|
14
15
|
# Pad content out
|
@@ -88,7 +89,7 @@ module Verse
|
|
88
89
|
end
|
89
90
|
|
90
91
|
def display_width(string)
|
91
|
-
UnicodeUtils.display_width(string)
|
92
|
+
UnicodeUtils.display_width(@sanitizer.sanitize(string))
|
92
93
|
end
|
93
94
|
end # Padding
|
94
95
|
end # Verse
|
data/lib/verse/sanitizer.rb
CHANGED
@@ -17,6 +17,18 @@ module Verse
|
|
17
17
|
text.gsub(ANSI_MATCHER, '')
|
18
18
|
end
|
19
19
|
|
20
|
+
# Check if string is an ANSI code
|
21
|
+
#
|
22
|
+
# @param [String] string
|
23
|
+
# the string to check
|
24
|
+
#
|
25
|
+
# @return [Boolean]
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def ansi?(string)
|
29
|
+
!!(string =~ /^(\[)?\033(\[)?[;?\d]*[\dA-Za-z]([\];])?$/)
|
30
|
+
end
|
31
|
+
|
20
32
|
# Replace separator with whitespace
|
21
33
|
#
|
22
34
|
# @example
|
data/lib/verse/truncation.rb
CHANGED
@@ -22,7 +22,7 @@ module Verse
|
|
22
22
|
#
|
23
23
|
# @api public
|
24
24
|
def initialize(text, options = {})
|
25
|
-
@text = text
|
25
|
+
@text = text.dup.freeze
|
26
26
|
@sanitizer = Sanitizer.new
|
27
27
|
@separator = options.fetch(:separator) { nil }
|
28
28
|
@trailing = options.fetch(:trailing) { DEFAULT_TRAILING }
|
@@ -60,18 +60,20 @@ module Verse
|
|
60
60
|
if display_width(text) <= truncate_at.to_i || truncate_at.to_i.zero?
|
61
61
|
return text.dup
|
62
62
|
end
|
63
|
-
trail
|
64
|
-
separation
|
63
|
+
trail = options.fetch(:trailing) { trailing }
|
64
|
+
separation = options.fetch(:separator) { separator }
|
65
|
+
width = display_width(text)
|
65
66
|
sanitized_text = @sanitizer.sanitize(text)
|
66
|
-
width = display_width(sanitized_text)
|
67
67
|
|
68
68
|
return text if width <= truncate_at
|
69
69
|
|
70
70
|
length_without_trailing = truncate_at - display_width(trail)
|
71
|
-
chars =
|
71
|
+
chars = to_chars(sanitized_text).to_a
|
72
72
|
stop = chars[0, length_without_trailing].rindex(separation)
|
73
|
-
|
74
|
-
|
73
|
+
slice_length = stop || length_without_trailing
|
74
|
+
sliced_chars = chars[0, slice_length]
|
75
|
+
original_chars = to_chars(text).to_a[0, 3 * slice_length]
|
76
|
+
shorten(original_chars, sliced_chars, length_without_trailing).join + trail
|
75
77
|
end
|
76
78
|
|
77
79
|
protected
|
@@ -83,22 +85,34 @@ module Verse
|
|
83
85
|
# @return [String]
|
84
86
|
#
|
85
87
|
# @api private
|
86
|
-
def shorten(chars, length_without_trailing)
|
88
|
+
def shorten(original_chars, chars, length_without_trailing)
|
87
89
|
truncated = []
|
88
90
|
char_width = display_width(chars[0])
|
89
91
|
while length_without_trailing - char_width > 0
|
92
|
+
orig_char = original_chars.shift
|
90
93
|
char = chars.shift
|
91
94
|
break unless char
|
95
|
+
while orig_char != char # consume ansi
|
96
|
+
ansi = true
|
97
|
+
truncated << orig_char
|
98
|
+
orig_char = original_chars.shift
|
99
|
+
end
|
92
100
|
truncated << char
|
93
101
|
char_width = display_width(char)
|
94
102
|
length_without_trailing -= char_width
|
95
103
|
end
|
104
|
+
truncated << ["\e[0m"] if ansi
|
96
105
|
truncated
|
97
106
|
end
|
98
107
|
|
108
|
+
# @api private
|
109
|
+
def to_chars(text)
|
110
|
+
UnicodeUtils.each_grapheme(text).to_a
|
111
|
+
end
|
112
|
+
|
99
113
|
# @api private
|
100
114
|
def display_width(string)
|
101
|
-
UnicodeUtils.display_width(string)
|
115
|
+
UnicodeUtils.display_width(@sanitizer.sanitize(string))
|
102
116
|
end
|
103
117
|
end # Truncation
|
104
118
|
end # Verse
|
data/lib/verse/version.rb
CHANGED
data/lib/verse/wrapping.rb
CHANGED
@@ -43,11 +43,19 @@ module Verse
|
|
43
43
|
if text.length < wrap_at.to_i || wrap_at.to_i.zero?
|
44
44
|
return text
|
45
45
|
end
|
46
|
+
ansi_stack = []
|
46
47
|
text.split(NEWLINE, -1).map do |paragraph|
|
47
|
-
format_paragraph(paragraph, wrap_at)
|
48
|
+
format_paragraph(paragraph, wrap_at, ansi_stack)
|
48
49
|
end * NEWLINE
|
49
50
|
end
|
50
51
|
|
52
|
+
protected
|
53
|
+
|
54
|
+
# The text to wrap
|
55
|
+
#
|
56
|
+
# @api private
|
57
|
+
attr_reader :text
|
58
|
+
|
51
59
|
# Format paragraph to be maximum of wrap_at length
|
52
60
|
#
|
53
61
|
# @param [String] paragraph
|
@@ -59,7 +67,7 @@ module Verse
|
|
59
67
|
# the wrapped lines
|
60
68
|
#
|
61
69
|
# @api private
|
62
|
-
def format_paragraph(paragraph, wrap_at)
|
70
|
+
def format_paragraph(paragraph, wrap_at, ansi_stack)
|
63
71
|
cleared_para = @sanitizer.replace(paragraph)
|
64
72
|
lines = []
|
65
73
|
line = ''
|
@@ -69,7 +77,25 @@ module Verse
|
|
69
77
|
char_length = 0 # visible char length
|
70
78
|
text_length = display_width(cleared_para)
|
71
79
|
total_length = 0
|
80
|
+
ansi = ''
|
81
|
+
matched = nil
|
72
82
|
UnicodeUtils.each_grapheme(cleared_para) do |char|
|
83
|
+
if char == ANSI # found ansi
|
84
|
+
ansi << char && next
|
85
|
+
end
|
86
|
+
|
87
|
+
if ansi.length > 0
|
88
|
+
ansi << char
|
89
|
+
if @sanitizer.ansi?(ansi) # we found ansi let's consume
|
90
|
+
matched = ansi
|
91
|
+
elsif matched
|
92
|
+
ansi_stack << [matched[0...-1], line_length + word_length]
|
93
|
+
matched = nil
|
94
|
+
ansi = ''
|
95
|
+
end
|
96
|
+
next if ansi.length > 0
|
97
|
+
end
|
98
|
+
|
73
99
|
char_length = display_width(char)
|
74
100
|
total_length += char_length
|
75
101
|
if line_length + word_length + char_length <= wrap_at
|
@@ -86,41 +112,72 @@ module Verse
|
|
86
112
|
end
|
87
113
|
|
88
114
|
if char == SPACE # ends with space
|
89
|
-
lines << line
|
115
|
+
lines << insert_ansi(ansi_stack, line)
|
90
116
|
line = ''
|
91
117
|
line_length = 0
|
92
|
-
word
|
93
|
-
word_length
|
118
|
+
word += char
|
119
|
+
word_length += char_length
|
94
120
|
elsif word_length + char_length <= wrap_at
|
95
|
-
lines << line
|
121
|
+
lines << insert_ansi(ansi_stack, line)
|
96
122
|
line = word + char
|
97
123
|
line_length = word_length + char_length
|
98
124
|
word = ''
|
99
125
|
word_length = 0
|
100
126
|
else # hyphenate word - too long to fit a line
|
101
|
-
lines << word
|
127
|
+
lines << insert_ansi(ansi_stack, word)
|
102
128
|
line_length = 0
|
103
129
|
word = char
|
104
130
|
word_length = char_length
|
105
131
|
end
|
106
132
|
end
|
107
|
-
lines << line unless line.empty?
|
108
|
-
lines << word unless word.empty?
|
133
|
+
lines << insert_ansi(ansi_stack, line) unless line.empty?
|
134
|
+
lines << insert_ansi(ansi_stack, word) unless word.empty?
|
109
135
|
lines
|
110
136
|
end
|
111
137
|
|
112
|
-
|
113
|
-
|
114
|
-
#
|
138
|
+
# Insert ANSI code into string
|
139
|
+
#
|
140
|
+
# Check if there are any ANSI states, if present
|
141
|
+
# insert ANSI codes at given positions unwinding the stack.
|
142
|
+
#
|
143
|
+
# @param [Array[Array[String, Integer]]] ansi_stack
|
144
|
+
# the ANSI codes to apply
|
145
|
+
#
|
146
|
+
# @param [String] string
|
147
|
+
# the string to insert ANSI codes into
|
148
|
+
#
|
149
|
+
# @return [String]
|
115
150
|
#
|
116
151
|
# @api private
|
117
|
-
|
152
|
+
def insert_ansi(ansi_stack, string)
|
153
|
+
return string if ansi_stack.empty?
|
154
|
+
to_remove = 0
|
155
|
+
reset_index = -1
|
156
|
+
output = string.dup
|
157
|
+
resetting = false
|
158
|
+
ansi_stack.reverse_each do |state|
|
159
|
+
if state[0] =~ /#{Regexp.quote(RESET)}/
|
160
|
+
resetting = true
|
161
|
+
reset_index = state[1]
|
162
|
+
to_remove += 2
|
163
|
+
next
|
164
|
+
elsif !resetting
|
165
|
+
reset_index = -1
|
166
|
+
resetting = false
|
167
|
+
end
|
168
|
+
|
169
|
+
color, color_index = *state
|
170
|
+
output.insert(reset_index, RESET).insert(color_index, color)
|
171
|
+
end
|
172
|
+
ansi_stack.pop(to_remove) # remove used states
|
173
|
+
output
|
174
|
+
end
|
118
175
|
|
119
176
|
# Visible width of string
|
120
177
|
#
|
121
178
|
# @api private
|
122
179
|
def display_width(string)
|
123
|
-
UnicodeUtils.display_width(string)
|
180
|
+
UnicodeUtils.display_width(@sanitizer.sanitize(string))
|
124
181
|
end
|
125
182
|
end # Wrapping
|
126
183
|
end # Verse
|
@@ -29,6 +29,12 @@ RSpec.describe Verse::Alignment, '.align' do
|
|
29
29
|
expect(alignment.center(20)).to eq(" こんにちは ")
|
30
30
|
end
|
31
31
|
|
32
|
+
it "centers ansi line" do
|
33
|
+
text = "\e[32mthe madness of men\e[0m"
|
34
|
+
alignment = Verse::Alignment.new(text)
|
35
|
+
expect(alignment.center(22)).to eq(" \e[32mthe madness of men\e[0m ")
|
36
|
+
end
|
37
|
+
|
32
38
|
it "centers multiline text" do
|
33
39
|
text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
|
34
40
|
alignment = Verse::Alignment.new(text)
|
@@ -51,6 +57,17 @@ RSpec.describe Verse::Alignment, '.align' do
|
|
51
57
|
].join)
|
52
58
|
end
|
53
59
|
|
60
|
+
it "centers ansi text" do
|
61
|
+
text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
|
62
|
+
alignment = Verse::Alignment.new(text)
|
63
|
+
expect(alignment.center(40)).to eq([
|
64
|
+
" for \e[35mthere\e[0m is no folly of the beast \n",
|
65
|
+
" of the \e[33mearth\e0m which \n",
|
66
|
+
" is \e[34mnot infinitely\e[0m \n",
|
67
|
+
" \e[33moutdone\e[0m by the madness of men "
|
68
|
+
].join)
|
69
|
+
end
|
70
|
+
|
54
71
|
it "centers multiline text with fill of '*'" do
|
55
72
|
text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
|
56
73
|
alignment = Verse::Alignment.new(text, fill: '*')
|
@@ -20,6 +20,12 @@ RSpec.describe Verse::Alignment, '.left' do
|
|
20
20
|
expect(alignment.align(20, :left)).to eq("こんにちは ")
|
21
21
|
end
|
22
22
|
|
23
|
+
it "left justifies ansi line" do
|
24
|
+
text = "\e[32mthe madness of men\e[0m"
|
25
|
+
alignment = Verse::Alignment.new(text)
|
26
|
+
expect(alignment.align(22, :left)).to eq("\e[32mthe madness of men\e[0m ")
|
27
|
+
end
|
28
|
+
|
23
29
|
it "aligns multiline text to left" do
|
24
30
|
text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
|
25
31
|
alignment = Verse::Alignment.new(text)
|
@@ -42,7 +48,18 @@ RSpec.describe Verse::Alignment, '.left' do
|
|
42
48
|
].join)
|
43
49
|
end
|
44
50
|
|
45
|
-
it "
|
51
|
+
it "left justifies ansi text" do
|
52
|
+
text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
|
53
|
+
alignment = Verse::Alignment.new(text)
|
54
|
+
expect(alignment.left(40)).to eq([
|
55
|
+
"for \e[35mthere\e[0m is no folly of the beast \n",
|
56
|
+
"of the \e[33mearth\e0m which \n",
|
57
|
+
"is \e[34mnot infinitely\e[0m \n",
|
58
|
+
"\e[33moutdone\e[0m by the madness of men "
|
59
|
+
].join)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "left justifies multiline text with fill of '*'" do
|
46
63
|
text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
|
47
64
|
alignment = Verse::Alignment.new(text, fill: '*')
|
48
65
|
expect(alignment.left(40)).to eq([
|
@@ -20,6 +20,12 @@ RSpec.describe Verse::Alignment, '.right' do
|
|
20
20
|
expect(alignment.align(20, :right)).to eq(" こんにちは")
|
21
21
|
end
|
22
22
|
|
23
|
+
it "right justifies utf line" do
|
24
|
+
text = "\e[32mthe madness of men\e[0m"
|
25
|
+
alignment = Verse::Alignment.new(text)
|
26
|
+
expect(alignment.align(22, :right)).to eq(" \e[32mthe madness of men\e[0m")
|
27
|
+
end
|
28
|
+
|
23
29
|
it "aligns multiline text to left" do
|
24
30
|
text = "for there is no folly of the beast\n of the earth which\n is not infinitely\n outdone by the madness of men"
|
25
31
|
alignment = Verse::Alignment.new(text)
|
@@ -42,7 +48,18 @@ RSpec.describe Verse::Alignment, '.right' do
|
|
42
48
|
].join)
|
43
49
|
end
|
44
50
|
|
45
|
-
it "
|
51
|
+
it "right justfies ansi text" do
|
52
|
+
text = "for \e[35mthere\e[0m is no folly of the beast\nof the \e[33mearth\e0m which\nis \e[34mnot infinitely\e[0m\n\e[33moutdone\e[0m by the madness of men"
|
53
|
+
alignment = Verse::Alignment.new(text)
|
54
|
+
expect(alignment.right(40)).to eq([
|
55
|
+
" for \e[35mthere\e[0m is no folly of the beast\n",
|
56
|
+
" of the \e[33mearth\e0m which\n",
|
57
|
+
" is \e[34mnot infinitely\e[0m\n",
|
58
|
+
" \e[33moutdone\e[0m by the madness of men"
|
59
|
+
].join)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "right justifies multiline text with fill of '*'" do
|
46
63
|
text = "for there is no folly of the beast\nof the earth which\nis not infinitely\noutdone by the madness of men"
|
47
64
|
alignment = Verse::Alignment.new(text, fill: '*')
|
48
65
|
expect(alignment.right(40)).to eq([
|
@@ -51,4 +51,17 @@ RSpec.describe Verse::Padding, '.pad' do
|
|
51
51
|
" ",
|
52
52
|
].join("\n"))
|
53
53
|
end
|
54
|
+
|
55
|
+
it "pads ANSI codes inside content" do
|
56
|
+
text = "It is \e[35mthe easiest\e[0m thing\nin the \e[34mworld\e[0m for a man\nto look as if he had \na great \e[33msecret\e[0m in him."
|
57
|
+
padding = Verse::Padding.new(text, padding: [1,1,1,1])
|
58
|
+
expect(padding.pad()).to eq([
|
59
|
+
" ",
|
60
|
+
" It is \e[35mthe easiest\e[0m thing ",
|
61
|
+
" in the \e[34mworld\e[0m for a man ",
|
62
|
+
" to look as if he had ",
|
63
|
+
" a great \e[33msecret\e[0m in him. ",
|
64
|
+
" ",
|
65
|
+
].join("\n"))
|
66
|
+
end
|
54
67
|
end
|
@@ -56,9 +56,9 @@ RSpec.describe Verse::Truncation, '.truncate' do
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it 'correctly truncates with ANSI characters' do
|
59
|
-
text = "
|
59
|
+
text = "I try \e[34mall things\e[0m, I achieve what I can"
|
60
60
|
truncation = Verse::Truncation.new(text)
|
61
|
-
expect(truncation.truncate).to eq(
|
61
|
+
expect(truncation.truncate(18)).to eq("I try \e[34mall things\e[0m…")
|
62
62
|
end
|
63
63
|
|
64
64
|
it "finishes on word boundary" do
|
@@ -30,6 +30,21 @@ RSpec.describe Verse::Wrapping, '.wrap' do
|
|
30
30
|
].join("\n"))
|
31
31
|
end
|
32
32
|
|
33
|
+
it "preserves newlines" do
|
34
|
+
text = "It is not down\n on any map;\n true places never are."
|
35
|
+
wrapping = Verse::Wrapping.new(text)
|
36
|
+
expect(wrapping.wrap(10)).to eq([
|
37
|
+
"It is not ",
|
38
|
+
"down",
|
39
|
+
" on any ",
|
40
|
+
"map;",
|
41
|
+
" true ",
|
42
|
+
"places ",
|
43
|
+
"never are."
|
44
|
+
].join("\n"))
|
45
|
+
end
|
46
|
+
|
47
|
+
|
33
48
|
it "wraps ascii text" do
|
34
49
|
text = "for there is no folly of the beast of the earth which is not infinitely outdone by the madness of men "
|
35
50
|
wrapping = Verse::Wrapping.new(text)
|
@@ -100,4 +115,46 @@ RSpec.describe Verse::Wrapping, '.wrap' do
|
|
100
115
|
].join("\n"))
|
101
116
|
end
|
102
117
|
end
|
118
|
+
|
119
|
+
context 'with ANSI codes' do
|
120
|
+
it "wraps ANSI chars" do
|
121
|
+
text = "\e[32;44mIgnorance is the parent of fear.\e[0m"
|
122
|
+
wrapping = Verse::Wrapping.new(text)
|
123
|
+
expect(wrapping.wrap(14)).to eq([
|
124
|
+
"\e[32;44mIgnorance is \e[0m",
|
125
|
+
"\e[32;44mthe parent of \e[0m",
|
126
|
+
"\e[32;44mfear.\e[0m",
|
127
|
+
].join("\n"))
|
128
|
+
end
|
129
|
+
|
130
|
+
it "wraps ANSI in the middle of text" do
|
131
|
+
text = "Ignorance is the \e[32mparent\e[0m of fear."
|
132
|
+
wrapping = Verse::Wrapping.new(text)
|
133
|
+
expect(wrapping.wrap(14)).to eq([
|
134
|
+
"Ignorance is ",
|
135
|
+
"the \e[32mparent\e[0m of ",
|
136
|
+
"fear.",
|
137
|
+
].join("\n"))
|
138
|
+
end
|
139
|
+
|
140
|
+
it "wraps multline ANSI codes" do
|
141
|
+
text = "\e32;44mMulti\nLine\nContent.\e[0m"
|
142
|
+
wrapping = Verse::Wrapping.new(text)
|
143
|
+
expect(wrapping.wrap(14)).to eq([
|
144
|
+
"\e32;44mMulti\e[0m",
|
145
|
+
"\e32;44mLine\e[0m",
|
146
|
+
"\e32;44mContent.\e[0m",
|
147
|
+
].join("\n"))
|
148
|
+
end
|
149
|
+
|
150
|
+
it "wraps multiple ANSI codes in a single line" do
|
151
|
+
text = "Talk \e[32mnot\e[0m to me of \e[33mblasphemy\e[0m, man; I'd \e[35mstrike the sun\e[0m if it insulted me."
|
152
|
+
wrapping = Verse::Wrapping.new(text)
|
153
|
+
expect(wrapping.wrap(30)).to eq([
|
154
|
+
"Talk \e[32mnot\e[0m to me of \e[33mblasphemy\e[0m, ",
|
155
|
+
"man; I'd \e[35mstrike the sun\e[0m if it ",
|
156
|
+
"insulted me."
|
157
|
+
].join("\n"))
|
158
|
+
end
|
159
|
+
end
|
103
160
|
end
|
data/verse.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = [""]
|
11
11
|
spec.summary = %q{Text transformations such as truncation, wrapping, aligning, indentation and grouping of words.}
|
12
12
|
spec.description = %q{Text transformations such as truncation, wrapping, aligning, indentation and grouping of words.}
|
13
|
-
spec.homepage = ""
|
13
|
+
spec.homepage = "https://github.com/peter-murach/verse"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: verse
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Piotr Murach
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: unicode_utils
|
@@ -72,6 +72,7 @@ files:
|
|
72
72
|
- spec/unit/padder/accessors_spec.rb
|
73
73
|
- spec/unit/padder/parse_spec.rb
|
74
74
|
- spec/unit/padding/pad_spec.rb
|
75
|
+
- spec/unit/sanitizer/ansi_spec.rb
|
75
76
|
- spec/unit/sanitizer/replace_spec.rb
|
76
77
|
- spec/unit/sanitizer/sanitize_spec.rb
|
77
78
|
- spec/unit/truncate_spec.rb
|
@@ -83,7 +84,7 @@ files:
|
|
83
84
|
- tasks/coverage.rake
|
84
85
|
- tasks/spec.rake
|
85
86
|
- verse.gemspec
|
86
|
-
homepage:
|
87
|
+
homepage: https://github.com/peter-murach/verse
|
87
88
|
licenses:
|
88
89
|
- MIT
|
89
90
|
metadata: {}
|
@@ -118,6 +119,7 @@ test_files:
|
|
118
119
|
- spec/unit/padder/accessors_spec.rb
|
119
120
|
- spec/unit/padder/parse_spec.rb
|
120
121
|
- spec/unit/padding/pad_spec.rb
|
122
|
+
- spec/unit/sanitizer/ansi_spec.rb
|
121
123
|
- spec/unit/sanitizer/replace_spec.rb
|
122
124
|
- spec/unit/sanitizer/sanitize_spec.rb
|
123
125
|
- spec/unit/truncate_spec.rb
|