|
|
“Replacement for Frontier's string.wrap function” |
|||
| From: | Seth Dillingham | In Response To: | Top of Thread. |
| Date Posted: | Monday, March 20, 2006 12:20:20 AM | Replies: | 1 |
| Enclosures: | workspace.stringWrap.ftsc (11K) | ||
Frontier's string.wrap is a nearly useless verb, and is especially weird because it does the opposite of what its name suggests: it actually unwraps a wrapped string. In other words, you pass in a message, like an email with hard returns at the end of every line, and it strips out the hard returns within the paragraphs and leaves the double-spaced paragraphs alone.
So, string.wrap( "a\rb\rc\r\rd" ) returns "a b c\r\rd".
I'm rewriting it in a way that maintains backwards compatibility but adds the following features:
The first version is script-based, just because it's a little easier to write. When I know it is correct, I'm going to port it to the kernel.
It seems to be working correctly for me, but I'd really appreciate it if a few more people would look it over before I kernelize it! Post your comments here (preferred), or send them to me in email.
2006/03/21
First, of course, you need the script. You can download it here, or just copy-and-paste it (from the very end of this page) into your own copy of Frontier. Testing instructions follow:
Note that the current version returns a string with mac line endings. Period. It wouldn't be difficult to use the current platform's endings, or to let you specify which line endings to use when you call the script, but this is good enough for now. (If you're on windows, run the results through string.replaceAll( wrapped, cr, "\r\n" ) after calling workspace.stringWrap()).
«2006/03/19 by Seth Dillingham.
«Description:
«Re-wrap a string to any maximum width-per-line.
«2+ returns in a row are treated as a paragraph break and are maintained.
«Optionally indents all lines in a paragraph according to the indentation of the first line of that paragraph.
«Optionally, can indent all lines in all paragraphs with any string you provide
«while still maintaining the maximum line length, of course
«Parameters:
«s [string]: the string to be re-wrapped
«maxWidth [number]: (infinity) the maximum width of each line in the final output
«flKeepIndent [boolean]: (false) should all lines in a paragraph have the same indent as the first line?
«indent [string]: (empty string) any string, prepended to every line in the output...
«- is automatically included in maxLength calculations
«- IGNORED if flKeepIndent is true
«Return:
«[string]: the re-wrapped string
«Errors:
«
«Revisions:
«2006/03/21 by Seth Dillingham.
«Updated to use improved routines from stringWrapQuoted
«Added optional indent param (ignored if flKeepIndent is true)
«[BUG] Blank lines with spaces or tabs would cause the following paragraph to be indented. Fixed.
«Thomas Creedon found this bug.
«~~~~~~~~~~~~~~~~~~~~~~~~
on stringWrap( s, maxWidth = infinity, flKeepIndent = false, indent = "" ) {
local ( tabSpaces = 4 );
local ( i = 1, ct = 0 );
local ( lastspace = 0 );
local ( sz = 0, indentsz = 0 );
local ( nextline = "", nextSpace = "" );
local ( output = "" );
on addBlankLines( ixStart ) {
local ( ix = ixStart, ctFound = 0 );
local ( ixEnd, nextIndent );
while ( ix < sz ) {
ixEnd = findNextIndent( ix );
nextIndent = string.mid( s, ix, ixEnd - ix );
if ( lineIsBlank( ix ) ) {
if ( flKeepIndent ) {
output = output + ( nextIndent + cr );}
else {
output = output + ( indent + cr );};
ix = ixEnd + 1}
else {
break}};
i = ix};
on lineIsblank( ixStart ) {
local ( ix = ixStart );
while ( ix < sz ) {
case s[ ix ] {
' ';
tab;
'>' {
ix++};
cr {
break}}
else {
return false}};
return true};
on stripWhitespace( t ) {
return string.replaceAll( string.replaceAll( t, ' ', "" ), tab, "" )};
on skipWhitespace( ixStart ) {
local ( ix = ixStart );
while ( ix < sz ) {
case s[ ix ] {
' ';
tab {
ix++}}
else {
break}};
return ix};
on findNextIndent( ixStart, adrSize = nil ) {
local ( ix = ixStart );
local ( isz = 0 );
while ( ix <= sz ) {
case s[ ix ] {
' ' {
ix++;
isz++};
tab {
ix++;
isz = isz + ( tabSpaces - ( isz % tabSpaces ) )}}
else {
break}};
if ( adrSize ) {
adrSize^ = isz};
return ix};
on getIndent( ixStart ) {
local ( isz = 0 );
local ( ix = findNextIndent( ixStart, @isz ) );
if ( flKeepIndent ) {
indent = string.mid( s, ixStart, ix - ixStart );
indentsz = isz};
lastSpace = ix - 1;
return ix};
on nextIndentMatchesOldIndent( ixStart ) {
local ( ixEnd = findNextIndent( ixStart ) );
local ( newIndent = string.mid( s, ixStart, ixEnd - ixStart ) );
return ( newIndent == indent )};
on addWordToNextLine( ) {
local ( nextwhite = lastspace );
while ( i <= sz ) {
case s[ i ] {
' ';
tab;
cr;
lf {
nextwhite = i;
break}}
else {
i++}};
if ( ct + ( i - lastspace - 1 ) > maxWidth ) {
if ( ct > indentsz ) { // ct has one or more words, so we don't include this word
i = lastSpace + 1;
return false}};
nextline = nextline + ( nextspace + string.mid( s, lastspace + 1, i - lastspace - 1 ) );
ct = ct + ( i - lastspace - 1 );
lastspace = nextwhite;
return ( i < sz )};
on addSpaceToNextLine( startIx, adrFlParaBreak ) {
local ( ix = startix );
local ( ctSpaces = 0, space = "" );
while ( ( ix < sz ) and ( ct < maxWidth ) ) {
case s[ ix ] {
' ' {
ix++;
ctSpaces++;
space = space + ' ';};
cr {
if ( lineIsBlank( ix + 1 ) ) { // if it's blank, then it's a parragraph separator
adrFlParaBreak^ = true;
i = ix;
return false}
else {
ix = skipWhitespace( ix + 1 );
lastspace = ix - 1;
if ( ctSpaces == 0 ) { // this is a cheatin' way to skip whitespace at the end of a line
ctSpaces++;
space = space + ' '}}};
tab {
ix++;
ctSpaces = ctSpaces + ( tabSpaces - ( ct + ctSpaces ) % tabSpaces );
space = space + tab}}
else {
break}};
if ( ( ( ix <= sz ) and ( s[ ix ] == cr ) ) or ( ct >= maxWidth ) ) { // skip remaining white space
while ( ix < sz ) {
case s[ ix ] {
' ';
tab {
ix++};
cr {
if ( nextIndentMatchesOldIndent( ix + 1 ) ) { // if the next line starts with the same quote/indent, then skip it
if ( lineIsBlank( ix + 1 ) ) {
adrFlParaBreak^ = true;
i = ix;
return false}
else {
ix = ix + sizeof( indent ) + 1;
lastspace = ix - 1}}}}
else {
break}}};
if ( ct + ctSpaces >= maxWidth ) {
nextSpace = "";
i = ix;
return false}
else {
lastspace = ix - 1;
nextSpace = space;
ct = ct + ctSpaces;
i = ix;
return true}};
on getNextLine() {
local ( flParaBreak = false );
nextline = indent;
nextspace = "";
ct = indentsz;
loop {
if ( not addWordToNextLine( ) ) {
break};
if ( not addSpaceToNextLine( i, @flParaBreak ) ) {
break}};
output = output + ( nextline + cr );;
if ( i >= sz ) {
return false}
else {
return not flParaBreak}};
on getNextParagraph() {
i = getIndent( i );
loop {
if ( not getNextLine() ) {
if ( i >= sz ) {
return false}
else {
break}}};
addBlankLines( ++i );
return true};
bundle { // init
if ( maxWidth < 1 ) {
ScriptError( "maxWidth must be greater than or equal to 1" )};
s = string.replaceAll( string.replaceAll( s, "r\n", cr ), lf, cr );
sz = sizeof( s );
if ( sizeof( indent ) != 0 ) { // get the width of the indent
local ( ix = 1, isz = 0 );
indent = string.replaceAll( indent, lf, "" );
while ( ix <= sizeof( indent ) ) {
case indent[ ix ] {
tab {
isz = isz + ( tabSpaces - ( isz % tabSpaces ) )};
cr { // reset isz to 0
isz = 0}}
else {
isz++};
ix++};
indentsz = isz}};
loop {
if ( not getNextParagraph() ) {
break};
if ( i >= sz ) {
break}};
return output};
bundle { // test code
local ( wrapWidth = 80 );
local ( unwrapped, wrapped );
unwrapped = clipboard.getValue( stringType );
wrapped = stringWrap( unwrapped, wrapWidth, false );
clipboard.putValue( wrapped )}
«wp.newTextObject( wrapped, @temp.wrapped )
«window.open( @temp.wrapped )
There are no trackbacks.
|
TruerWords
is Seth Dillingham's personal web site. More than the sum of my parts. |