How To Unformat Your Code (Like A Pro)
At this past CFUNITED, big Dan Vega told me about a little project he wanted to show in the Demo Derby. The conversation went a little something like this (in meaning, not word for word):
Ben, I love using your code; but I hate that it takes me 30 minutes to remove all of your freaking formatting. So, I'm trying to create a ColdFusion Builder (CFBuilder) extension for the demo derby that will allow people to remove all of your formatting with a single click!
I heard this and I thought it was just hilarious! Unfortunately, Dan wasn't able to finish the project in time and, I think, ultimately was having some problems with the regular expressions that would be required. Since I love regular expressions, and I think it's just a funny concept, I figured I would jump in and see what I could do. Plus, you have to admit that there's some delicious irony over using someone's code to help destroy it.
Over yesterday and today, I created the "Obsessive" ColdFusion component which has one key method, unformat(), that takes the obsessively formatted code that you want to unformat and returns the newly non-formatted string.
Obsessive.cfc
<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( "" ) />
<!--- 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="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="string"
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="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="removeSpacing"
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 ) &
(chr( 13 ) & chr( 10 ))
) />
</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 ) &
(chr( 13 ) & chr( 10 ))
) />
</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()
.removeSpacing()
.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(
"(?s)<!---(((?!--->).)+)--->"
) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Unwrap the comments. --->
<cfset this.replaceMatch(
"<!---" &
reReplace(
this.getMatch( 1 ),
"[ \t]*(\r\n?|\n)[ \t]*",
" ",
"all"
) &
"--->"
) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- We want to remove any "Define" comments. --->
<cfset this.createMatcher(
"(?s)(<!---) Define(((?!--->).)+)(--->)"
) />
<!--- Remove matches. --->
<cfset this.removeMatches() />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
</cfcomponent>
This ColdFusion component is targeted towards tag-based CFML, so it won't work on HTML at the moment. But, that functionality could easily be added. To see this action, I figured I would turn the Obsessive.cfc lose on itself, like some sort of freaky mobius strip of code (where's M.C. Escher when you need a drawing of a piece of code devouring itself?).
<!--- Read in the objsessive component as a test. --->
<cfset code = fileRead( expandPath( "./Obsessive.cfc" ) ) />
<!--- Create an instance of the obsessive unformatter. --->
<cfset objsessive = createObject( "component", "Obsessive" )
.init()
/>
<!---
Remove all of the meticulous formatting, thought, and theory
that Ben Nadel has poured into writing his code.
--->
<cfset unformattedCode = objsessive.unformat( code ) />
<!--- Output the unformatted code. --->
<cfoutput>
<pre>#htmlEditFormat( unformattedCode )#</pre>
</cfoutput>
After we run the code, and remove all of the obsessively thorough design, here is the resultant source code:
Obsessive.cfc (Unformatted 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( "" ) />
<!--- 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.">
<cfargument name="pattern" type="string" required="true" hint="I am the regular expression pattern." />
<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="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="string" output="false" hint="I get the current match.">
<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="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="removeSpacing" access="public" returntype="any" output="false" hint="I remove certain spacing from the code.">
<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 ) & (chr( 13 ) & chr( 10 )) ) />
</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 ) & (chr( 13 ) & chr( 10 )) ) />
</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.">
<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.">
<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.">
<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).">
<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().removeSpacing().getCode() />
</cffunction>
<cffunction name="unformatCFTag" access="public" returntype="any" output="false" hint="I unformat the ColdFusion tag with the given name.">
<cfargument name="tagName" type="string" required="true" hint="I am the name of the tag being unformatted." />
<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.">
<cfset var local = {} />
<!--- Get all multiline comments. --->
<cfset this.createMatcher( "(?s)<!---(((?!--->).)+)--->" ) />
<!--- Loop over each of the match. --->
<cfloop condition="this.findMatch()">
<!--- Unwrap the comments. --->
<cfset this.replaceMatch( "<!---" & reReplace( this.getMatch( 1 ), "[ \t]*(\r\n?|\n)[ \t]*", " ", "all" ) & "--->" ) />
</cfloop>
<!--- --------------------------------------------- --->
<!--- We want to remove any "Define" comments. --->
<cfset this.createMatcher( "(?s)(<!---) Define(((?!--->).)+)(--->)" ) />
<!--- Remove matches. --->
<cfset this.removeMatches() />
<!--- Return this component reference. --->
<cfreturn this />
</cffunction>
</cfcomponent>
So there you go! From what I have tested, it seems to work pretty well. I leave it up to big Dan now to take this and turn it into a CFBuilder extension ;)
Want to use code from this post? Check out the license.
Reader Comments
Now that's really funny because my favorite thing about your code is the formatting since it makes it that much easier to read!!!
@Jean,
Awesome - I'm glad you like. I'm so used to writing and thinking in that way, that I have a hard time reading code until I've given it the once-over.
The fact that you developed a function to unformat your own code, is just priceless.
You should include this as an option for your code snippets or an option to download the file in a unformatted way. Just created another service for users!
Hmmm maybe now you should see if there is a way to take someone else's code and turn it into your style of coding... hmmm That could be hard not knowing what their style is but that would be a fun challenge. <grin>
@Michael,
Thanks :)
@Nathan,
Hmmm, interesting idea. My first instinct would be to say that it would be hard because I don't know how valid the code is... but it's not like I'm parsing it as XML; I wonder how hard it would be.
@Nathan,
That is what I was thinking too
Funny stuff. I second Michael's suggestion for a link to download the deobsessified version of your code :)
I thought this was a fun little project until I saw:
# <!---
# Remove all of the meticulous formatting, thought, and theory
# that Ben Nadel has poured into writing his code.
# --->
Now I feel like this is the straw that broke Ben's spirit. :(
;)
@Todd,
Ha ha, just having some fun with it ;)
Ben great job, I happen to like your coding style and find it very easy to understand and follow.
@Nathan,
I totally agree, I (like the rest of you I assume) am pretty meticulous when it comes to code formatting and making it easy to read and follow the logic. There's nothing I dislike more about my job than when I get someone else's code and I have to spend time re-formatting it so that I can make sense of the logic. An "Obsessive Format" to clean up the unformatted would be fantastic.
@Chuck, I would love it as well. I was not joking I am very serious I love well formatted code.
@Chuck, @Nathan,
Well, now I feel like it's a challenge.... I accept.
I tried to go *back* the other way. Adding formatting is actually a much more complicated problem than removing formatting. Anyway, here is what I could put together with a minimal amount of time:
www.bennadel.com/blog/1749-How-To-UN-Unformat-Your-Code-Like-A-Pro-.htm
The timing of this post is just uncanny. I spent the last 15-20 minutes manually un-formatting my "Ben Nadel" style code within a CFC of mine. I was really digging the readability a few weeks ago, but I found that if you implement code folding in CF Builder you then open up a CFC and get a bunch of lines that look like this
<cffunction...
<cffunction...
<cffunction...
Ack!
A feasible alternative to code folding, fortunately, is the Outline View, so technically not all would be lost. The vertical scrolling of all the code did start to get a bit painful though, but that's mainly cause I had a bunch of cfargument and cfqueryparam tags for some functions. So I guess what I'm trying to say here is, I'll never be quite like Ben! But that's okay, right? :)
Regardless of my trip-up with this approach, I think your code formatting is PERFECT for your blog post. I'm not a huge fan of "scroll" overflows within code blocks, and it gets annoying when you have long statements going from left to right anyway. Keep it up, bro.
One question. Is your code coloring dynamic or do you manually fill in all the span tags? I'm guessing it is the former as you have downloadable code snippets and duplicating all those code samples would be a bear.
LMAO - this was pretty funny! I have to admit - I also love to reformat code so I can read it. My boss used to tell me to leave my OCD at home. Now I don't feel so bad after reading everyone else's comments :)
I think it would be way easier to just use this http://www.logichammer.com/html-formatter/
He just released v3 and it rocks.
Derek, I think you might be right - sweet! Thanks for the link :)
@Derek,
That does look pretty cool. Looks like he's put a lot of time into it.