Skip to content

Instantly share code, notes, and snippets.

@bennadel
Created March 25, 2014 11:04
Show Gist options
  • Save bennadel/9759429 to your computer and use it in GitHub Desktop.
Save bennadel/9759429 to your computer and use it in GitHub Desktop.
How To UN-Unformat Your Code (Like A Pro)
<cfcomponent
output="false"
hint="I am a component written by Ben Nadel to help people remove Ben's own meticulous formatting from his code - some kids just aren't cool enough ;)">
<cffunction
name="init"
access="public"
returntype="any"
output="false"
hint="I initialize this component.">
<!--- I am the code that being unformatted. --->
<cfset this.setCode( "" ) />
<!--- Set up some constants. --->
<cfset this.tab = chr( 9 ) />
<cfset this.newLine = (chr( 13 ) & chr( 10 )) />
<cfset this.newLine2 = (this.newLine & this.newLine) />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="createMatcher"
access="public"
returntype="any"
output="false"
hint="I create a pattern matcher based on the given regular expression and store it as the internal matcher.">
<!--- Define arguments. --->
<cfargument
name="pattern"
type="string"
required="true"
hint="I am the regular expression pattern."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Compile the regular expression for the tag. --->
<cfset local.pattern = createObject(
"java",
"java.util.regex.Pattern"
).compile(
javaCast( "string", arguments.pattern )
) />
<!---
Create a pattern matcher for the given pattern and
the current code.
--->
<cfset local.matcher = local.pattern.matcher(
this.getCode()
) />
<!--- Store the current matcher. --->
<cfset this.setMatcher( local.matcher ) />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="findMatch"
access="public"
returntype="boolean"
output="false"
hint="I call find on the current matcher.">
<!--- Check to see if the next match was found. --->
<cfif this.getMatcher().find()>
<!--- The next match was found. --->
<cfreturn true />
<cfelse>
<!---
The next match was not found. At this point, we
want to append any existing code into the buffer
and then comit the buffer back into the code.
--->
<!--- Append the tail. --->
<cfset this.getMatcher().appendTail( this.getBuffer() ) />
<!--- Commit the buffer into the code. --->
<cfset this.setCode( this.getBuffer().toString() ) />
<!--- Return false. --->
<cfreturn false />
</cfif>
</cffunction>
<cffunction
name="format"
access="public"
returntype="string"
output="false"
hint="I format the given code (adding Ben's code meticulous code formatting - as much as possible).">
<!--- Define arguments. --->
<cfargument
name="code"
type="string"
required="true"
hint="I am the code being formatted."
/>
<!--- Set the code, format it, and return it. --->
<cfreturn this
.setCode( arguments.code )
.formatCFTag( "CFArgument", true )
.formatCFTag( "CFCookie", true )
.formatCFTag( "CFComponent" )
.formatCFTag( "CFContent", true )
.formatCFTag( "CFDirectory", true )
.formatCFTag( "CFFunction" )
.formatCFTag( "CFHeader", true )
.formatCFTag( "CFHttp" )
.formatCFTag( "CFHttpParam", true )
.formatCFTag( "CFImage", true )
.formatCFTag( "CFLocation", true )
.formatCFTag( "CFLock" )
.formatCFTag( "CFLoop" )
.formatCFTag( "CFMail" )
.formatCFTag( "CFMailParam", true )
.formatCFTag( "CFParam", true )
.formatCFTag( "CFSetting", true )
.formatCFTag( "CFZip" )
.formatCFTag( "CFZipParam", true )
.formatSpacing()
.formatComments()
.getCode()
/>
</cffunction>
<cffunction
name="formatCFTag"
access="public"
returntype="any"
output="false"
hint="I format the given ColdFusoin tag with the given name.">
<!--- Define arguments. --->
<cfargument
name="tagName"
type="string"
required="true"
hint="I am the name of the tag being formatted."
/>
<cfargument
name="isSelfClosing"
type="boolean"
required="false"
default="false"
hint="I flag whether or not this type of flag should be self-closing."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Create the pattern for the tag. --->
<cfsavecontent variable="local.regex">(?ixm)
<cfoutput>
^(\p{Blank}*)<#arguments.tagName#
(
[^>"']
|
"([^"]|"")*"
|
'([^']|'')*'
)*
>
</cfoutput>
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Get the tag code. --->
<cfset local.tagCode = trim( this.getMatch() ) />
<!--- Parse the tag code into a data set. --->
<cfset local.tagData = this.parseTag(
trim( this.getMatch() )
) />
<!--- Get the leading space. --->
<cfset local.leadingSpace = this.getMatch( 1 ) />
<!---
Normalize the spaces (replacing 4 spaces with a
tab). There's simply no hope for code that was
using two-space indents.
--->
<cfset local.leadingSpace = reReplace(
local.leadingSpace,
" {4}",
this.tab,
"all"
) />
<!---
Now that we have replaced-in tabs, remove any left
over spaces from the leading space.
--->
<cfset local.leadingSpace = reReplace(
local.leadingSpace,
" +",
"",
"all"
) />
<!---
Create a constant for the indent of the sub-lines.
This will be the leading space plus an additional
tab character.
--->
<cfset local.subIndent = (local.leadingSpace & this.tab) />
<!---
Now that have the tag data parsed and the leading
space (indent), we can now construct the new tag
output.
--->
<cfset local.newCode = (
local.leadingSpace &
"<" &
lCase( local.tagData.name )
) />
<!--- Loop over each attribute to append to code. --->
<cfloop
index="local.attribute"
array="#local.tagData.attributes#">
<!--- Add the attribute on its own line. --->
<cfset local.newCode &= (
this.newLine &
local.subIndent &
local.attribute.name &
"=" &
local.attribute.value
) />
</cfloop>
<!---
When closing the tag, check to see if this tag is
self closing.
--->
<cfif arguments.isSelfClosing>
<!--- Add the self-closing cap on the next line. --->
<cfset local.newCode &= (
this.newLine &
local.subIndent &
"/>"
) />
<cfelse>
<!--- Add the cap on the same line. --->
<cfset local.newCode &= ">" />
</cfif>
<!---
If there was only ONE attribute in the tag,
override the spacing so that it is all on one line.
--->
<cfif (arrayLen( local.tagData.attributes ) eq 1)>
<!--- Remove line breaks. --->
<cfset local.newCode = reReplace(
local.newCode,
"[ \t]*[\r\n]+[ \t]*",
" ",
"all"
) />
</cfif>
<!--- Replace with the new tag code. --->
<cfset this.replaceMatch( local.newCode ) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="formatComments"
access="public"
returntype="any"
output="false"
hint="I format the comments.">
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Get all multiline comments. --->
<cfset this.createMatcher(
"(?ms)^([ \t]*)<!---(((?!--->).)+)--->"
) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Get the leading space. --->
<cfset local.leadingSpace = this.getMatch( 1 ) />
<!---
Normalize the spaces (replacing 4 spaces with a
tab). There's simply no hope for code that was
using two-space indents.
--->
<cfset local.leadingSpace = reReplace(
local.leadingSpace,
" {4}",
this.tab,
"all"
) />
<!---
Now that we have replaced-in tabs, remove any left
over spaces from the leading space.
--->
<cfset local.leadingSpace = reReplace(
local.leadingSpace,
" +",
"",
"all"
) />
<!---
Create a constant for the indent of the sub-lines.
This will be the leading space plus an additional
tab character.
--->
<cfset local.subIndent = (local.leadingSpace & this.tab) />
<!--- Get the comment itself. --->
<cfset local.comment = trim( this.getMatch( 2 ) ) />
<!--- Remove any line breaks from the comment. --->
<cfset local.comment = reReplace(
local.comment,
"\s+",
" ",
"all"
) />
<!---
Check to see how long the comment is. If it is
longer than 70 characters, we will break it up
into several lines. Otherwise, we will leave it
as a single line.
--->
<cfif (len( local.comment ) gt 70)>
<!--- Build up the new comment. --->
<cfset local.newComment = (
local.leadingSpace &
"<!---" &
this.newLine
) />
<!---
Break the comment into words of length no
greater than 70.
--->
<cfset local.lines = reMatch(
".{0,70}\b(?!\s)",
local.comment
) />
<!--- Add the lines to the new comment. --->
<cfset local.newComment &= (
local.subIndent &
arrayToList(
local.lines,
(this.newLine & local.subIndent)
) &
this.newLine
) />
<!--- Close the comment. --->
<cfset local.newComment &= (
local.leadingSpace &
"--->"
) />
<!--- Repalce in the new comment. --->
<cfset this.replaceMatch( local.newComment ) />
<cfelse>
<!--- Leave as a single-line comment. --->
<cfset this.replaceMatch(
local.leadingSpace &
"<!--- " &
local.comment &
" --->"
) />
</cfif>
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the tag. --->
<cfsavecontent variable="local.regex">(?ixm)
<cfoutput>
(
<(cffunction)
([^>"]|"([^"]|"")*")*
>.*
(\r\n?|\n)
(^\p{Blank}*$(\r\n?|\n))*
)
^(\p{Blank}*)<(cfargument)
</cfoutput>
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Add the pre-argument comment. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.getMatch( 8 ) &
"<!--- Define arguments. --->" &
this.newLine &
this.getMatch( 8 ) &
"<" &
this.getMatch( 9 )
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the local scope. --->
<cfsavecontent variable="local.regex">(?ixm)
^(\p{Blank}*)<(cfset)\s+var\s+local
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Add the local scope comment. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
"<!--- Define the local scope. --->" &
this.newLine &
this.getMatch( 1 ) &
"<cfset var local"
) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="formatSpacing"
access="public"
returntype="any"
output="false"
hint="I add certain spacing to the code.">
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
<(cfcomponent)
([^>"]|"([^"]|"")*")*
>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))*
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.newLine2
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
<(cf(function|argument))
([^>"]|"([^"]|"")*")*
>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))*
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.newLine
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
</(cffunction)>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))*
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.newLine2
) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="getBuffer"
access="public"
returntype="any"
output="false"
hint="I return the current string buffer being used in conjunction with the current regular expression pattern matcher.">
<!--- Return the buffer. --->
<cfreturn variables.buffer />
</cffunction>
<cffunction
name="getCode"
access="public"
returntype="string"
output="false"
hint="I return the code.">
<!--- Return this current code state. --->
<cfreturn variables.code />
</cffunction>
<cffunction
name="getMatch"
access="public"
returntype="any"
output="false"
hint="I get the current match.">
<!--- Define arguments. --->
<cfargument
name="group"
type="string"
required="false"
default="0"
hint="I am the group being returned - default to zero, which is the entire match."
/>
<!---
Return the current match from the active
pattern matcher.
--->
<cfreturn this.getMatcher().group(
javaCast( "int", arguments.group )
) />
</cffunction>
<cffunction
name="getMatcher"
access="public"
returntype="any"
output="false"
hint="I return the current regular expression pattern matcher.">
<!--- Return the current matcher. --->
<cfreturn variables.matcher />
</cffunction>
<cffunction
name="initBuffer"
access="public"
returntype="any"
output="false"
hint="I create a new string buffer to be used with the current regular expression pattern matcher.">
<!--- Store a new buffer. --->
<cfset variables.buffer = createObject(
"java",
"java.lang.StringBuffer"
).init()
/>
<!--- Return this component reference. --->
<cfreturn />
</cffunction>
<cffunction
name="parseTag"
access="public"
returntype="struct"
output="false"
hint="I parse the given tag code.">
<!--- Define arguments. --->
<cfargument
name="code"
type="string"
required="true"
hint="I am the flat tag code being parsed."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Create the tag structure. --->
<cfset local.tag = {
name = "",
attributes = []
} />
<!---
Create a new pattern for matching the tokens of
the tag, starting with the tag name and then the
attribute pairs.
--->
<cfsavecontent variable="local.regex">(?x)
^<[^\s]+
|
\s+
[\w:]+
\s*=\s*
(
"[^"]*(""[^"]*)*"
|
'[^']*(''[^']*)*'
|
[^\s]+
)
</cfsavecontent>
<!--- Extract the tag tokens. --->
<cfset local.tokens = reMatch(
local.regex,
arguments.code
) />
<!--- Set the name of the tag as the first token. --->
<cfset local.tag.name = reReplace(
lCase( local.tokens[ 1 ] ),
"^<",
"",
"one"
) />
<!---
Delete the tag name token so we don't process it as
one of the tag attributs.
--->
<cfset arrayDeleteAt( local.tokens, 1 ) />
<!---
Now, let's look over the rest of the tokens and parse
the name-value pairs.
--->
<cfloop
index="local.token"
array="#local.tokens#">
<!--- Create a new attribute as dicated by the equals. --->
<cfset local.attribute = {
name = lcase( trim( listFirst( local.token, "=" ) ) ),
value = trim( listRest( local.token, "=" ) )
} />
<!---
Further clean the value to make sure that value is
only double quoted.
--->
<cfset local.attribute.value = (
"""" &
reReplace(
reReplace(
local.attribute.value,
"^[""']|[""']$",
"",
"all"
),
"""""|""(?!"")",
"""""",
"all"
) &
""""
) />
<!--- Add the parsed attribute to the collection. --->
<cfset arrayAppend(
local.tag.attributes,
local.attribute
) />
</cfloop>
<!--- Return the parsed tag data. --->
<cfreturn local.tag />
</cffunction>
<cffunction
name="removeMatches"
access="public"
returntype="any"
output="false"
hint="I remove all of the matches.">
<!--- Loop over all the matches. --->
<cfloop condition="this.findMatch()">
<!---
Replace the current match with the empty string.
This will remove it from the code.
--->
<cfset this.replaceMatch( "" ) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="replaceMatch"
access="public"
returntype="any"
output="false"
hint="I replace the given string in leu of the current match.">
<!--- Define arguments. --->
<cfargument
name="value"
type="string"
required="true"
hint="I am the value being replaced in."
/>
<!--- Replace the current string. --->
<cfset this.getMatcher().appendReplacement(
this.getBuffer(),
reReplace(
arguments.value,
"([\\\$])",
"\\\1",
"all"
)
)/>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="setCode"
access="public"
returntype="any"
output="false"
hint="I store the given code as part of the object state.">
<!--- Define arguments. --->
<cfargument
name="code"
type="string"
required="true"
hint="I am the code being stored."
/>
<!--- Store the code. --->
<cfset variables.code = arguments.code />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="setMatcher"
access="public"
returntype="any"
output="false"
hint="I store the given matcher as the internal matcher.">
<!--- Define arguments. --->
<cfargument
name="matcher"
type="any"
required="true"
hint="I am the matcher being stored."
/>
<!--- Store the matcher. --->
<cfset variables.matcher = arguments.matcher />
<!---
Whenever we store a new matcher, we want to re-init
the buffer since this matcher will be used with a
replacement mechanism.
--->
<cfset this.initBuffer() />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="unformat"
access="public"
returntype="string"
output="false"
hint="I unformat the given code (removing Ben's code meticulous code formatting).">
<!--- Define arguments. --->
<cfargument
name="code"
type="string"
required="true"
hint="I am the code being unformatted."
/>
<!--- Set the code, unformat it, and return it. --->
<cfreturn this
.setCode( arguments.code )
.unformatCFTag( "CFArgument" )
.unformatCFTag( "CFCookie" )
.unformatCFTag( "CFComponent" )
.unformatCFTag( "CFContent" )
.unformatCFTag( "CFDirectory" )
.unformatCFTag( "CFFunction" )
.unformatCFTag( "CFHeader" )
.unformatCFTag( "CFHttp" )
.unformatCFTag( "CFHttpParam" )
.unformatCFTag( "CFIF" )
.unformatCFTag( "CFImage" )
.unformatCFTag( "CFLocation" )
.unformatCFTag( "CFLock" )
.unformatCFTag( "CFLoop" )
.unformatCFTag( "CFMail" )
.unformatCFTag( "CFMailParam" )
.unformatCFTag( "CFParam" )
.unformatCFTag( "CFReturn" )
.unformatCFTag( "CFSet" )
.unformatCFTag( "CFSetting" )
.unformatCFTag( "CFZip" )
.unformatCFTag( "CFZipParam" )
.unformatComments()
.unformatSpacing()
.getCode()
/>
</cffunction>
<cffunction
name="unformatCFTag"
access="public"
returntype="any"
output="false"
hint="I unformat the ColdFusion tag with the given name.">
<!--- Define arguments. --->
<cfargument
name="tagName"
type="string"
required="true"
hint="I am the name of the tag being unformatted."
/>
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Create the pattern for the tag. --->
<cfsavecontent variable="local.regex">(?ix)
<cfoutput>
<#arguments.tagName#
([^>"]|"([^"]|"")*")*
>
</cfoutput>
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Remove the line breaks. --->
<cfset local.replace = reReplace(
this.getMatch(),
"[\s\t]*[\r\n]+[\s\t]*",
" ",
"all"
) />
<!--- Remove any spaces before periods. --->
<cfset local.replace = reReplace(
local.replace,
" \.",
".",
"all"
) />
<!--- Replace match.. --->
<cfset this.replaceMatch( local.replace ) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="unformatComments"
access="public"
returntype="any"
output="false"
hint="I unformat the comments.">
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Get all multiline comments. --->
<cfset this.createMatcher(
"(?ms)^([ \t]*)<!---(((?!--->).)+)--->"
) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Unwrap the comments. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
"<!---" &
reReplace(
this.getMatch( 2 ),
"[ \t]*(\r\n?|\n)[ \t]*",
" ",
"all"
) &
"--->"
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- We want to remove any "Define" comments. --->
<cfset this.createMatcher(
"(?ms)^[ \t]*(<!---) Define(((?!--->).)+)(--->)"
) />
<!--- Remove matches. --->
<cfset this.removeMatches() />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
<cffunction
name="unformatSpacing"
access="public"
returntype="any"
output="false"
hint="I remove certain spacing from the code.">
<!--- Define the local scope. --->
<cfset var local = {} />
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
<(cfcomponent)
([^>"]|"([^"]|"")*")*
>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))+
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.newLine
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
<(cf(function|argument))
([^>"]|"([^"]|"")*")*
>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))+
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 )
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- Create the pattern for the spacing. --->
<cfsavecontent variable="local.regex">(?ixm)
(
</(cffunction)>
.*
(\r\n?|\n)
)
(^(\p{Blank})*$(\r\n?|\n))+
</cfsavecontent>
<!--- Create a new matcher. --->
<cfset this.createMatcher( local.regex ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Replace the post-tag empty lines. --->
<cfset this.replaceMatch(
this.getMatch( 1 ) &
this.newLine
) />
</cfloop>
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
</cfcomponent>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment