Reputation: 3265
I've been working on a formatter that will take a long string and format it into a series of lines broken off at the word within a certain character limit. For instance, He eats the bread, broken off at every 8 characters, would return something like:
He eats
the
bread
This is because "He eats" contains 7 characters, and "the bread" contains 9, so it has to break off at "the" and continue with "bread".
The script itself has been working wonderfully, thanks to the help of other members before. However, now I have a new challenge.
I'm utilizing a free-entry box for entering the series of lines. It's for a program called MUSHclient, if anyone is familiar with it. The utils.editbox opens up an editor text box, and allows free form, and returns the result as a string. For example:
result = utils.editbox("What are you typing?")
saves the typed response into result
which is a string
type. I want to put line breaks in properly, but I can't figure out how to do that. If I print(result)
, it returns something like the following:
This is the result of three separate paragraphs.
As you can see, there is a line break between each paragraph.
So the result contains line breaks, or control characters.
Now, I've managed to extrapolate the individual lines, but not the linebreaks in between, using the following code:
for line in result:gmatch("[^%c]+%c?") do
Send(note_wrap(line))
end
How can I extrapolate for linebreaks as well as textual content?
Edit For reference, the note_wrap
function is as follows:
function note_wrap(str, limit, indent, indent1)
indent = indent or ""
indent1 = indent1 or indent
limit = limit or 79
local here = 1-#indent1
local last_color = ''
return indent1..str:gsub("(%s+)()(%S+)()",
function(sp, st, word, fi)
local delta = 0
local color_before_current_word = last_color
word:gsub('()@([@%a])',
function(pos, c)
if c == '@' then
delta = delta + 1
elseif c == 'x' then
delta = delta + 5
last_color = word:sub(pos, pos+4)
else
delta = delta + 2
last_color = word:sub(pos, pos+1)
end
end)
here = here + delta
if fi-here > limit then
here = st - #indent + delta
return "\n"..indent..color_before_current_word..word
end
end)
end
The reason for this code is to be able to take @rHe eats @x123the bread, ignore the color codes (indicated by @<letter> or @x<digits>) and return the aforementioned result of:
He eats
the
bread
but insert the color codes so it then returns:
@rHe eats
@x123the
bread
If this can be modified to recognize new lines and put an actual new line in, that would be fabulous.
Edit 2 Tried Paul's solution, and it's not doing what I'm needing it to. I may not have been clear enough, so I'll try to clear it up in this edit.
When I type into the free form box, I want the information presented exactly as is, but formatted to break correctly AND maintain entered newlines. For example, if I write this in the free form box:
Mary had a little lamb, little lamb, little lamb
Mary had a little lamb
It's fleece was white as snow
using Paul's code, and setting string to 79, I get:
Mary had a little lamb, little lamb, little lamb
Mary had a little lamb
It's fleece was white as snow
And that's not what I want. I'd want it to return it just as it was written, and breaking line as necessary. So if I had a 20 character limit, it'd return:
Mary had a little
lamb, little lamb,
little lamb
Mary had a little
lamb
It's fleece was
white as snow
If I added manual line breaks, I'd want it to respect those, so if I hit return twice after the first line, it'd have a line break under the first line as well. For example, if I wrote this post in the free form box, I'd want it to respect every new paragraph and every proper line break as well. I hope this clears things up.
Upvotes: 4
Views: 17821
Reputation: 26744
I use something like this in my code (this assumes that \n
is the line separator):
local function formatUpToX(s, x, indent)
x = x or 79
indent = indent or ""
local t = {""}
local function cleanse(s) return s:gsub("@x%d%d%d",""):gsub("@r","") end
for prefix, word, suffix, newline in s:gmatch("([ \t]*)(%S*)([ \t]*)(\n?)") do
if #(cleanse(t[#t])) + #prefix + #cleanse(word) > x and #t > 0 then
table.insert(t, word..suffix) -- add new element
else -- add to the last element
t[#t] = t[#t]..prefix..word..suffix
end
if #newline > 0 then table.insert(t, "") end
end
return indent..table.concat(t, "\n"..indent)
end
print(formatUpToX(result, 20))
print(formatUpToX("@rHe eats @x123the bread", 8, " "))
The cleanse
function removes any markup that needs to be included in the string, but doesn't need to count against the limit.
For the example you have, I get the following output (using 20
as the limit for the first fragment and 8
for the second):
This is the result
of three separate
paragraphs.
As you can see,
there is a line
break between each
paragraph.
So the result
contains line
breaks, or control
characters.
@rHe eats
@x123the
bread
Upvotes: 1