ID:195086
 
//  Title: Word-Wrapping (Efficient Version)
// Credit to: Hiead
// Contributed by: Hiead

/*
I wrote this after looking at Jtgibson's wordwrap() proc.
-Starting- to think that "Spuzzum" stands for something along
the lines of "thinks of overcomplicated solutions to trivial
matters."

I probably wouldn't have written this, except for the fact
that Jt's wordwrap() is extremely (I will show you!) slow.
Sorry Spuzz! I'll imagine that this is another one of those
things that he had sitting around for years.

In the presence of a laziness to write anactual description of
the function in question, I'll use Jt's:

" This is a monospace word-wrapper. BYOND lacks any utilities
to determine the width of proportional fonts, so this feature
must remain monospace. "
*/


proc
wordwrap(string, width=80)
var/list/lines = new
// breaks will be associative; a TRUE value means to display it
var/list/breaks = list(" ", "\t", "-" = TRUE)

while(string) // string will gradually be pulled apart
// First step: pull out lines that fit within the width
var/pos = findtext(string, "\n")
while(pos && pos <= width)
lines.Add(copytext(string, 1, pos))
string = copytext(string, pos+1)
pos = findtext(string, "\n")

var/len = length(string)

if(len <= width) // If we're in the width, we're done
lines.Add(string)
break

var/broken = FALSE
// Step two: start at the end of the width, and come back
// until we find a break-point
BreakLoop:
for(var/index = width, index, index--)
for(var/linebreak in breaks)
var/lbLen = length(linebreak)

// If this break is too long, move on
if(index+lbLen > len)
continue

if(cmpText(linebreak, \
copytext(string, index, index+lbLen)))

lines.Add(copytext(string, 1, index + \
(breaks[linebreak] ? lbLen : 0)))

string = copytext(string, index+lbLen)
broken = TRUE
break BreakLoop

if(broken) // Time to start at step 1? Sure!
continue

// If nothing break-worthy was found, just pull a string
// that matches the width in length, and go back to step 1
else
lines.Add(copytext(string, 1, width+1))
string = copytext(string, width+1)

// Add each line to the return value
for(var/index in 1 to lines.len)
. += "[index != 1 ? "\n" : ""][lines[index]]"

///*
//Testing Code/Sample Implementation:

client/verb/test_wordwrap(message as message, wordwrap=80 as num)
src << "<tt>[wordwrap(message, wordwrap)]</tt>"

//*/

Bleh, on a certain couple of lines the display is wanting to add like 3 extra indents to the code (in the two places where I split code into two lines). I suppose people can always change that once they stick it in DM. :(

As for the promised speed comparison (for those of you that read the comments), it might go something like this: (note that the proc from [link] is Jtgibson's version)

#define DEBUG
// Go to Options->Profile World before running this verb
// After running it, press Refresh for the speed comparison
client/verb/Speed_Comparison()
var/msg = "Hello, once upon a time there was a man named Muppet.\nMuppet died. Boo-hoo. \
Muppet had no friends, and he was doomed to absolute failure from birth. And so he \
was an absolute failure at death, with no family and not a penny to his name. Sucks \
for him."


sleep(5)
for(var/i in 1 to 1000)
wordwrap_jt(msg)
// Rename Jtgibson's version to wordwrap_jt
sleep(5)
for(var/i in 1 to 1000)
wordwrap_hiead(msg)
// Rename my version to wordwrap_hiead


If you want to play around with those numbers, a bit, you might notice that at 1,000 calls of my version, Jt's would still be slower at only 100 calls (at least, it produces that locally).

Note to Jtgibson: Not trying to up-stage you, if you get worried about that; it's not my fault that you like to post all of your old and slow code for people to use/learn from! Seriously though, nothing against you. :P

Hiead
Heh. I actually added the "learn from" clause as an afterthought because most of the snippets were just intended as plug'n'play functions to solve little problems I had and I figured someone might also be able to take advantage of having a bunch of code to look at.

I'll go ahead and pass off my old version as being inefficient. =)
Here's my version. Offers a slight increase in speed.
proc/wordwrap(string, width=80)
var/list/lines = new
var/start = 1
var/end = width
var/list/breaks = list(" ", "\t", "-")
while(end<length(string) && end>start)
if(copytext(string, end, end+1) in breaks)
var/newline = findtext(string, "\n", start, end)
if(newline)
lines += copytext(string, start, newline)
start = ++newline
end = start+width
else
lines += copytext(string, start, end)
start = ++end
end += width-1
else end--
lines += copytext(string, start, length(string)+1)
for(var/index = 1, index<=lines.len, index++)
. += "[index != 1 ? "\n" : ""][lines[index]]"