Dedenting Text In ColdFusion
I've been on a real regular expression kick this week. Between my previous post on parsing Postmark Bounce details and creating custom trim functions, it seems that regular expressions make a daily appearance in my programming life. And, today is no exception. As a fun code kata, I want to try dedenting text in ColdFusion. Spoiler alert: I use regular expressions.
Dedenting text is the act of removing the leading whitespace from each line of text. However, my dedent()
function differs from my inlineTrim()
function in that it maintains the relative indentation across lines. That is, it will dedent the text only until any one of the lines no longer has leading whitespace.
Basically, I examine every line of text, find the one with least amount of indentation characters, and then remove that amount of indentation from all lines of text.
Here's my ColdFusion code. By default, it assumes the indentation character is the TAB
. However, for legacy contexts, it can be configured to use the SPACE
character if necessary.
<cfscript>
include "./utils.cfm"; // concat(), line(), and showWhitespace().
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
text = concat(
line( " We will find the minimum indentation width" ),
line( " and then remove that width" ),
line( " from each line of the content," ),
line( " such that the file remains" ),
line( " uniformly indented." )
);
// We're going to strip the leading tabs from the given content such that the line
// starting with "from" touches the left-edge of the content and the rest of the lines
// are all indented relative to that line.
echo( "<pre>" & showWhitespace( dedent( text ) ) & "</pre>" );
// ------------------------------------------------------------------------------- //
// ------------------------------------------------------------------------------- //
/**
* I remove indentation from the given input but leave the input such that the relative
* indentation across lines remains constant.
*/
public string function dedent(
required string input,
string indentation = "tab"
) {
var indentationCharacter = ( indentation == "tab" )
? chr( 9 ) // Tab.
: chr( 32 ) // Space.
;
// In order to figure out how much we can dedent the text, we must first locate
// the smallest amount of indentation that currently exists across all lines of
// text. However, we only care about lines of text that have non-indentation
// characters on them (ie, we want to skip over empty lines). As such, we're going
// to create a pattern that must end on non-indentation (or line-break) character.
var minWidth = input
.reMatch( "(?m)^#indentationCharacter#*[^#indentationCharacter#\r\n]" )
.map(
( linePrefix ) => {
return ( linePrefix.len() - 1 );
}
)
// NOTE: ArrayMin() returns zero if array is empty.
.min()
;
if ( ! minWidth ) {
return input;
}
// Now that we've found the smallest amount of indentation across all lines of
// text, we can remove exactly that amount of indentation from each line of text.
var dedentedInput = input
.reReplace( "(?m)^#indentationCharacter#{#minWidth#}", "", "all" )
;
return dedentedInput;
}
</cfscript>
Now, if we run this ColdFusion code, we get the following output (in which I've replaced all whitespace characters with visual characters for easier viewing):
--We.will.find.the.minimum.indentation.width+
-and.then.remove.that.width+
from.each.line.of.the.content,+
-such.that.the.file.remains+
--uniformly.indented.+
As you can see, the line starting with from
now touches the left-edge of the text block. And, all the lines surrounding it still have indentation; but, their indentation remains relative to the from
line.
The reason this concept came to mind has to do with the CFSaveContent
tag. When defining content using this tag in ColdFusion, the content must be indented in order to maintain an appropriate code aesthetic:
<cfoutput>
<cfsavecontent variable="text">
This code is indented with 2 tabs.
But, that's for aesthetics -
Not because it needs to be indented.
</cfsavecontent>
</cfoutput>
Given this context, I would need to dedent the resultant text
value by two tabs in order to end up with my intended content.
Want to use code from this post? Check out the license.
Reader Comments
This dedenting work wasn't entirely random - it was a stepping stone for my next post on creating a
CF_SaveFile
ColdFusion custom tag to save buffered content directly to a file (instead of to a variable):www.bennadel.com/blog/4638-cf-savefile-custom-tag-in-coldfusion.htm
Post A Comment — ❤️ I'd Love To Hear From You! ❤️
Post a Comment →