Skip to content

Commit

Permalink
Merge pull request #980 from yast/improve-text-helpers
Browse files Browse the repository at this point in the history
Improve the #wrap_text helper
  • Loading branch information
dgdavid committed Nov 18, 2019
2 parents 43753ef + 10a5344 commit 0f84dfb
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 48 deletions.
59 changes: 35 additions & 24 deletions library/general/src/lib/ui/text_helpers.rb
Expand Up @@ -20,36 +20,47 @@
module UI
# Provides a set of methods to manipulate and transform UI text
module TextHelpers
# Wrap given text breaking lines longer than given wrap size. It supports
# custom separator, max number of lines to split in and cut text to add
# as last line if cut was needed.
# Wrap text breaking lines in the first whitespace that does not exceed given line width
#
# @param [String] text to be wrapped
# @param [String] wrap size
# @param [Hash <String>] optional parameters as separator and prepend_text.
# @return [String] wrap text
def wrap_text(text, wrap = 76, separator: " ", prepend_text: "",
n_lines: nil, cut_text: nil)
lines = []
message_line = prepend_text
text.split(/\s+/).each_with_index do |t, i|
if !message_line.empty? && "#{message_line}#{t}".size > wrap
lines << message_line
message_line = ""
end
# Additionally, it also allows retrieving only an excerpt of the wrapped text according to the
# maximum number of lines indicated, adding one more with the cut_text text when it is given.
#
# @param text [String] text to be wrapped
# @param line_width [Integer] max line length
# @param n_lines [Integer, nil] the maximum number of lines
# @param cut_text [String] the omission text to be used when the text should be cut
#
# @return [String]
def wrap_text(text, line_width = 76, n_lines: nil, cut_text: "")
return text if line_width > text.length

message_line << separator if !message_line.empty? && i != 0
message_line << t
wrapped_text = text.lines.collect! do |line|
l = (line.length > line_width) ? line.gsub(/(.{1,#{line_width}})(?:\s+|$)/, "\\1\n") : line
l.strip
end

lines << message_line if !message_line.empty?
result = wrapped_text.join("\n")
result = head(result, n_lines, omission: cut_text) if n_lines
result
end

if n_lines && lines.size > n_lines
lines = lines[0..n_lines - 1]
lines << cut_text if cut_text
end
# Returns only the first requested lines of the given text
#
# If the omission param is given, an extra line holding it will be included
#
# @param text [String]
# @param max_lines [Integer]
# @param omission [String] the text to be added
#
# @return [String] the first requested lines if the text has more; full text otherwise
def head(text, max_lines, omission: "")
lines = text.lines

return text if lines.length <= max_lines

lines.join("\n")
result = text.lines[0...max_lines]
result << omission unless omission.empty?
result.join
end

# Wrap a given text in direction markers
Expand Down
93 changes: 69 additions & 24 deletions library/general/test/text_helpers_test.rb
Expand Up @@ -10,45 +10,90 @@ class TestTextHelpers

describe ::UI::TextHelpers do
subject { TestTextHelpers.new }
let(:text) do
"This is a long paragraph.
It contains a not_real_but_really_long_word which must not be broken
and the length of its longer lines is a little git greater than the default line width.
Let's see if it's work."
end

describe "#wrap_text" do
let(:devices) { ["eth0", "eth1", "eth2", "eth3", "a_very_long_device_name"] }
let(:more_devices) do
[
"enp5s0", "enp5s1", "enp5s2", "enp5s3",
"enp5s4", "enp5s5", "enp5s6", "enp5s7"
]
context "when the text does not exceed the line width" do
let(:text) { "A very short text." }

it "returns the same text" do
expect(subject.wrap_text(text)).to eq(text)
end
end

context "given a text" do
it "returns same text if it does not exceed the wrap size" do
text = "eth0, eth1, eth2, eth3, a_very_long_device_name"
context "when the text exceed the given line width" do
it "produces a text with lines no longer than given line width" do
line_width = 60
wrapped_text = subject.wrap_text(text, line_width)

expect(wrapped_text.lines.map(&:length)).to all(be < line_width)
end

it "respect present carriage returns" do
current_lines = text.lines.size

expect(subject.wrap_text(devices.join(", "))).to eql(text)
expect(subject.wrap_text(text).lines.size).to be > current_lines
end

context "and a line size" do
it "returns given text splitted in lines by given line size" do
text = "eth0, eth1, eth2,\n" \
"eth3,\n" \
"a_very_long_device_name"
it "does not break words" do
wrapped_text = subject.wrap_text(text)

expect(wrapped_text).to match(/it\'s/)
expect(wrapped_text).to match(/not_real_but_really_long_word/)
end

context "and a max number of lines is set (n_lines)" do
it "returns only the first n_lines" do
wrapped_text = subject.wrap_text(text, n_lines: 2)

expect(wrapped_text.lines.size).to eq(2)
expect(wrapped_text).to match(/^This is/)
expect(wrapped_text).to match(/broken$/)
end

context "with an ommission text (cut_text)" do
it "includes an additional line with the cut_text" do
omission = "..."
wrapped_text = subject.wrap_text(text, n_lines: 2, cut_text: omission)

expect(subject.wrap_text(devices.join(", "), 16)).to eql(text)
expect(wrapped_text.lines.size).to eq(3)
expect(wrapped_text).to match(/^This is/)
expect(wrapped_text.lines.last).to eq("...")
end
end
end
end
end

describe "#head" do
let(:omission_text) { "read more" }

context "and a number of lines and '...' as cut text" do
it "returns wrapped text until given line's number adding '...' as a new line" do
devices_s = (devices + more_devices).join(", ")
text = "eth0, eth1, eth2,\n" \
"eth3,\n" \
"a_very_long_device_name,\n" \
"..."
context "when the text has less lines than requested" do
it "returns the full text" do
expect(subject.head(text, 10)).to eq(text)
end

expect(subject.wrap_text(devices_s, 20, n_lines: 3, cut_text: "...")).to eql(text)
context "and the omision text is given" do
it "does not include the omission text" do
expect(subject.head(text, 10, omission: omission_text)).to_not include(omission_text)
end
end
end

context "when the text has more lines than requested" do
it "returns only the first requested lines" do
head = subject.head(text, 2)

expect(head.lines.size).to eq(2)
expect(head).to match(/^This is/)
expect(head).to match(/broken$/)
end
end
end

describe "#div_with_direction" do
Expand Down

0 comments on commit 0f84dfb

Please sign in to comment.