91 lines
2.0 KiB
Plaintext
91 lines
2.0 KiB
Plaintext
class SmartWordWrap {
|
||
|
||
has width = 80
|
||
|
||
method prepare_words(array, depth=0, callback) {
|
||
|
||
var root = []
|
||
var len = 0
|
||
var i = -1
|
||
|
||
var limit = array.end
|
||
while (++i <= limit) {
|
||
len += (var word_len = array[i].len)
|
||
|
||
if (len > width) {
|
||
if (word_len > width) {
|
||
len -= word_len
|
||
array.splice(i, 1, array[i].split(width)...)
|
||
limit = array.end
|
||
--i; next
|
||
}
|
||
break
|
||
}
|
||
|
||
root << [
|
||
array.first(i+1).join(' '),
|
||
self.prepare_words(array.ft(i+1), depth+1, callback)
|
||
]
|
||
|
||
if (depth.is_zero) {
|
||
callback(root[0])
|
||
root = []
|
||
}
|
||
|
||
break if (++len >= width)
|
||
}
|
||
|
||
root
|
||
}
|
||
|
||
method combine(root, path, callback) {
|
||
var key = path.shift
|
||
path.each { |value|
|
||
root << key
|
||
if (value.is_empty) {
|
||
callback(root)
|
||
}
|
||
else {
|
||
value.each { |item|
|
||
self.combine(root, item, callback)
|
||
}
|
||
}
|
||
root.pop
|
||
}
|
||
}
|
||
|
||
method wrap(text, width) {
|
||
|
||
self.width = width
|
||
var words = (text.kind_of(Array) ? text : text.words)
|
||
|
||
var best = Hash(
|
||
score => Inf,
|
||
value => [],
|
||
)
|
||
|
||
self.prepare_words(words, callback: { |path|
|
||
self.combine([], path, { |combination|
|
||
var score = 0
|
||
combination.ft(0, -2).each { |line|
|
||
score += (width - line.len -> sqr)
|
||
}
|
||
|
||
if (score < best{:score}) {
|
||
best{:score} = score
|
||
best{:value} = []+combination
|
||
}
|
||
})
|
||
})
|
||
|
||
best{:value}.join("\n")
|
||
}
|
||
}
|
||
|
||
var sww = SmartWordWrap();
|
||
|
||
var words = %w(aaa bb cc ddddd);
|
||
var wrapped = sww.wrap(words, 6);
|
||
|
||
say wrapped;
|