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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7531f5a29482d44a943c00b361f49e6673bdd9c7
4
- data.tar.gz: 1bc1d1680d157e66faf7a44ba9c3ed0a804e7828
3
+ metadata.gz: 91a9c0b0722656e5b2b160a14e68f9d228830120
4
+ data.tar.gz: 9397441752523d44ac885b7e5ad4b0e8faffbead
5
5
  SHA512:
6
- metadata.gz: 5348385613bff73636e6b5993ebcafbba3ce1093a0b15e01c7c089248b61c09175c1793c0af5e8fdf482b2c149a0e9d6ea5b3d190c4246c689d5deba220b0d8a
7
- data.tar.gz: f1605d77c8ffaf24695739ca4df6bb646834b0ccfd3c2b3a51641cd0a48ab1729fafb57a079bc08a86522ace2f3c419991f1ee44c9826fbbcb1a2ea7899ce4f7
6
+ metadata.gz: aa1fb93fb455d0d141616c0a0dd08886486331a92b5b165a3ec1852f228b89e8002fb078d9329b0c9325a758725004ebb14f74c5ba618145de9c29990b2b0519
7
+ data.tar.gz: bf4deb4d61427c35aab5d4522b31003a49e1b93388e176fd19533dfe3177e618126beee94893de210b14f64b5c51555b707cb7b39b1d17dd168ead985af54c6d
@@ -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
@@ -15,6 +15,8 @@ module Verse
15
15
  SPACE = ' '.freeze
16
16
  NEWLINE = "\n".freeze
17
17
  TAB = "\n".freeze
18
+ RESET = "\e[0m".freeze
19
+ ANSI = "\033".freeze
18
20
 
19
21
  SPACE_RE = %r{\s+}mo.freeze
20
22
  NEWLINE_RE = %r{\n}o.freeze
@@ -12,8 +12,9 @@ module Verse
12
12
  #
13
13
  # @api public
14
14
  def initialize(text, options = {})
15
- @text = text
16
- @fill = options.fetch(:fill) { SPACE }
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 - UnicodeUtils.display_width(text)
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 - UnicodeUtils.display_width(text)
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 = UnicodeUtils.display_width(text)
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
@@ -7,8 +7,9 @@ module Verse
7
7
  #
8
8
  # @api public
9
9
  def initialize(text, options = {})
10
- @text = text
11
- @padding = Padder.parse(options[: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
@@ -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
@@ -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 = options.fetch(:trailing) { trailing }
64
- separation = options.fetch(:separator) { separator }
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 = UnicodeUtils.each_grapheme(sanitized_text).to_a
71
+ chars = to_chars(sanitized_text).to_a
72
72
  stop = chars[0, length_without_trailing].rindex(separation)
73
- sliced_chars = chars[0, stop || length_without_trailing]
74
- shorten(sliced_chars, length_without_trailing).join + trail
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
@@ -1,5 +1,5 @@
1
1
  # coding: utf-8
2
2
 
3
3
  module Verse
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end # Verse
@@ -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 = word + char
93
- word_length = word_length + char_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
- protected
113
-
114
- # The text to wrap
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
- attr_reader :text
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 "centers multiline text with fill of '*'" do
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 "centers multiline text with fill of '*'" do
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
@@ -0,0 +1,11 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec_helper'
4
+
5
+ RSpec.describe Verse::Sanitizer, '.ansi?' do
6
+ subject(:sanitizer) { described_class.new }
7
+
8
+ it "checks if code is ansi" do
9
+ expect(sanitizer.ansi?("\e[0;33m")).to eq(true)
10
+ end
11
+ 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 = "This is a \e[1m\e[34mbold blue text\e[0m"
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(text)
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
@@ -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.3.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-02-28 00:00:00.000000000 Z
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