lib/strings/wrap.rb in strings-0.1.5 vs lib/strings/wrap.rb in strings-0.1.6
- old
+ new
@@ -7,35 +7,34 @@
require_relative 'fold'
module Strings
module Wrap
DEFAULT_WIDTH = 80
-
NEWLINE = "\n".freeze
+ SPACE = " ".freeze
+ LINE_BREAK = %r{\r\n|\r|\n}.freeze
+ LINE_BREAKS = "\r\n+|\r+|\n+".freeze
- SPACE = ' '.freeze
-
- LINE_BREAK = "\r\n+|\r+|\n+".freeze
-
# Wrap a text into lines no longer than wrap_at length.
# Preserves existing lines and existing word boundaries.
#
# @example
# Strings::Wrap.wrap("Some longish text", 8)
# # => >Some
# >longish
# >text
#
# @api public
- def wrap(text, wrap_at = DEFAULT_WIDTH)
- if text.length < wrap_at.to_i || wrap_at.to_i.zero?
+ def wrap(text, wrap_at = DEFAULT_WIDTH, separator: nil)
+ if text.scan(/[[:print:]]/).length < wrap_at.to_i || wrap_at.to_i.zero?
return text
end
ansi_stack = []
- text.split(%r{#{LINE_BREAK}}, -1).map do |paragraph|
+ sep = separator || text[LINE_BREAK] || NEWLINE
+ text.split(%r{#{LINE_BREAKS}}, -1).map do |paragraph|
format_paragraph(paragraph, wrap_at, ansi_stack)
- end * NEWLINE
+ end * sep
end
module_function :wrap
# Format paragraph to be maximum of wrap_at length
#
@@ -68,11 +67,16 @@
if Strings::ANSI.only_ansi?(ansi.join)
ansi_matched = true
elsif ansi_matched
ansi_stack << [ansi[0...-1].join, line_length + word_length]
ansi_matched = false
- ansi = []
+
+ if ansi.last == Strings::ANSI::CSI
+ ansi = [ansi.last]
+ else
+ ansi = []
+ end
end
next if ansi.length > 0
end
char_length = display_width(char)
@@ -89,68 +93,75 @@
end
next
end
if char == SPACE # ends with space
- lines << insert_ansi(ansi_stack, line.join)
+ lines << insert_ansi(line.join, ansi_stack)
line = []
line_length = 0
word << char
word_length += char_length
elsif word_length + char_length <= wrap_at
- lines << insert_ansi(ansi_stack, line.join)
+ lines << insert_ansi(line.join, ansi_stack)
line = [word.join + char]
line_length = word_length + char_length
word = []
word_length = 0
else # hyphenate word - too long to fit a line
- lines << insert_ansi(ansi_stack, word.join)
+ lines << insert_ansi(word.join, ansi_stack)
line_length = 0
word = [char]
word_length = char_length
end
end
- lines << insert_ansi(ansi_stack, line.join) unless line.empty?
- lines << insert_ansi(ansi_stack, word.join) unless word.empty?
+ lines << insert_ansi(line.join, ansi_stack) unless line.empty?
+ lines << insert_ansi(word.join, ansi_stack) unless word.empty?
lines
end
module_function :format_paragraph
# Insert ANSI code into string
#
# Check if there are any ANSI states, if present
# insert ANSI codes at given positions unwinding the stack.
#
- # @param [Array[Array[String, Integer]]] ansi_stack
- # the ANSI codes to apply
- #
# @param [String] string
# the string to insert ANSI codes into
#
+ # @param [Array[Array[String, Integer]]] ansi_stack
+ # the ANSI codes to apply
+ #
# @return [String]
#
# @api private
- def insert_ansi(ansi_stack, string)
+ def insert_ansi(string, ansi_stack = [])
return string if ansi_stack.empty?
- to_remove = 0
- reset_index = -1
- output = string.dup
- resetting = false
- ansi_stack.reverse_each do |state|
- if state[0] =~ /#{Regexp.quote(Strings::ANSI::RESET)}/
- resetting = true
- reset_index = state[1]
- to_remove += 2
+ return string if string.empty?
+
+ new_stack = []
+ output = string.dup
+ length = string.size
+ matched_reset = false
+ ansi_reset = Strings::ANSI::RESET
+
+ # Reversed so that string index don't count ansi
+ ansi_stack.reverse_each do |ansi|
+ if ansi[0] =~ /#{Regexp.quote(ansi_reset)}/
+ matched_reset = true
+ output.insert(ansi[1], ansi_reset)
next
- elsif !resetting
- reset_index = -1
- resetting = false
+ elsif !matched_reset # ansi without reset
+ matched_reset = false
+ new_stack << ansi # keep the ansi
+ next if ansi[1] == length
+ output.insert(-1, ansi_reset) # add reset at the end
end
- color, color_index = *state
- output.insert(reset_index, Strings::ANSI::RESET).insert(color_index, color)
+ output.insert(ansi[1], ansi[0])
end
- ansi_stack.pop(to_remove) # remove used states
+
+ ansi_stack.replace(new_stack)
+
output
end
module_function :insert_ansi
# Visible width of a string