Hey everyone, welcome to the Flash AS3 blog. This is my first article so let’s start with something interesting, runtime syntax highlighting in AS3 using regular expressions!
Before we start, lets take a look at the end. Here is an example of the end result.
First a little background info…
In AS3 Adobe have given us plenty of new tools and features. The one that interests us today is the RegExp class, this is a TopLevel class and information about it can be found here.
The RegExp class finally gives flash the power of Regular Expressions. If you don’t know what a regular expression is then this link is a useful read. Basically a regular expression is a string that defines search criteria.
Now that you have read those links and know everything there is to know about regular expressions (or were so confused by what you have read that you gave up).
Lets get down to business…
The RegExp.exec function is similar to the indexOf function, you give it something to search for and it returns the position of a match. Obviously there is more to it than that or we would just use the indexOf function, so here is how we use RegExp.exec to find all the “Keywords” (any words that we want to make blue in this case)
var KeyWords:RegExp = new RegExp("(\b)(Some|Text){1}(\.|(\s)+|;|,|\(|\)|\]|\[|\{|\}){1}","g");
var result:Object =
exp.exec("Some Example Text")
while (result != null) {
trace(result.index);
result = exp.exec("Some Example Text");
}
This example will output the start position of "Some" and the start position of "Text". Lets examine the regular expression (\b) used here means a word break this could be a /n /r (line break and carriage return) or any of the space characters. This was added to prevent partial words being returned, for example without the word break a function called getText would return the start of Text, which is not what we want. At this point you may be asking yourself what are the two back slashes for? Well, the word break in regular expressions is simply b the slash means to use the special term for b, otherwise it would just search for the letter b, also if you want to use a special character that already has a meaning in regular expressions, you must first "escape" it with a back slash. Unfortunately for us Flash uses similar methodology in its strings, so for us to use a back slash in a string we must first escape it with a back slash. This double escaping can get confusing so it might be worth writing the actual expression in a comment next to your "flash string" expression.
The next part of the regular expression in this example "(Some|Text)" is the words that we are searching for, again like Flash it uses the pipe symbol as an or operator (only a single pipe though) you can have as many keywords between the brackets separated by pipes as you wish. The {1} simply means to run this section of the expression once.
The final part of the expression is what separates the keyword at the end, a dot, a bracket etc. so here I have included all the separators I could think of and double escaped them.
When executing a regular expression you can specify flags that provide certain search options. For example with reference to case sensitivity the "g" flag stands for global and using it will make the next execution start from the previously found index. We use a while loop in this example to find all the keywords in the string, when no more words are found RegExp.exec returns null and the function is finished.
Great, but how do I turn this into something useful?
Let's examine what is highlighted. In Adobe's highlighting, they use 5 colours; Black, Blue, Green, Grey and Red. Let's assume that the text will start black, so we need to highlight four different colours. Blue and Red are just different kinds of keywords so a modified version of the example above will work for those, but there is also Green, which is Strings. Strings can be double quoted or single quoted so we will need 2 regular expressions for Green, and then there is Grey, Comments. Comments can be single line "//" multi line "/* */" or multi-line on a single line. We will need at least 3 regular expressions for comments. Some syntax highlighters also highlight brackets in red, although Adobe's doesn't we will add this optional functionality too.
First lets create the different text formats for each colour.
var KeywordFormat:TextFormat = new TextFormat(null,null,0x0000FF); var SystemObjectFormat:TextFormat = new TextFormat(null,null,0xFF0000); var BracketFormat:TextFormat = new TextFormat(null,null,0x000000); var StringFormat:TextFormat = new TextFormat(null,null,0x009900); var CommentFormat:TextFormat = new TextFormat(null,null,0x666666); var DefaultFormat:TextFormat = new TextFormat(null,null,0x000000);
We will also need a list of keywords, I have prepared some here, these arent all the keywords but there is enough to be getting on with.
var AS3KeyWords:String = "addEventListener|align|ArgumentError|arguments|Array|as|AS3|Boolean|break|case|catch|class|Class|const|continue|data|Date|decodeURI|decodeURIComponent|default|DefinitionError|delete|do|dynamic|each|else|encodeURI|encodeURIComponent|Error|escape|EvalError|extends|false|finally|flash_proxy|for|function|get|getLineOffset|height|if|implements|import|in|include|index|Infinity|instanceof|interface|internal|intrinsic|is|isFinite|isNaN|isXMLName|label|load|namespace|NaN|native|new|null|Null|object_proxy|override|package|parseFloat|parseInt|private|protected|public|return|set|static|super|switch|this|throw|trace|true|try|typeof|undefined|unescape|use|var|void|while|with|Accessibility|AccessibilityProperties|ActionScriptVersion|ActivityEvent|AntiAliasType|ApplicationDomain|AsyncErrorEvent|AVM1Movie|BevelFilter|Bitmap|BitmapData|BitmapDataChannel|BitmapFilter|BitmapFilterQuality|BitmapFilterType|BlendMode|BlurFilter|ByteArray|Camera|Capabilities|CapsStyle|ColorMatrixFilter|ColorTransform|ContextMenu|ContextMenuBuiltInItems|ContextMenuEvent|ContextMenuItem|ConvolutionFilter|CSMSettings|DataEvent|Dictionary|DisplacementMapFilter|DisplacementMapFilterMode|DisplayObject|DisplayObjectContainer|DropShadowFilter|Endian|EOFError|ErrorEvent|Event|EventDispatcher|EventPhase|ExternalInterface|FileFilter|FileReference|FileReferenceList|FocusEvent|Font|FontStyle|FontType|FrameLabel|Function|GlowFilter|GradientBevelFilter|GradientGlowFilter|GradientType|Graphics|GridFitType|HTTPStatusEvent|IBitmapDrawable|ID3Info|IDataInput|IDataOutput|IDynamicPropertyOutput|IDynamicPropertyWriter|IEventDispatcher|IExternalizable||IllegalOperationError|IME|IMEConversionMode|IMEEvent|int|InteractiveObject|InterpolationMethod|InvalidSWFError|IOError|IOErrorEvent|JointStyle|Keyboard|KeyboardEvent|KeyLocation|LineScaleMode|Loader|LoaderContext|LoaderInfo|LocalConnection|Math|Matrix|MemoryError|Microphone|MorphShape|Mouse|MouseEvent|MovieClip|Namespace|NetConnection|NetStatusEvent|NetStream|Number|Object|ObjectEncoding|PixelSnapping|Point|PrintJob|PrintJobOptions|PrintJobOrientation|ProgressEvent|Proxy|QName|RangeError|Rectangle|ReferenceError|RegExp|resize|result|Responder|scaleMode|Scene|ScriptTimeoutError|Security|SecurityDomain|SecurityError|SecurityErrorEvent|SecurityPanel|setTextFormat|Shape|SharedObject|SharedObjectFlushStatus|SimpleButton|Socket|Sound|SoundChannel|SoundLoaderContext|SoundMixer|SoundTransform|SpreadMethod|Sprite|StackOverflowError|Stage|stageHeight|stageWidth|StageAlign|StageQuality|StageScaleMode|StaticText|StatusEvent|String|StyleSheet|SWFVersion|SyncEvent|SyntaxError|System|text|TextColorType|TextDisplayMode|TextEvent|TextField|TextFieldAutoSize|TextFieldType|TextFormat|TextFormatAlign|TextLineMetrics|TextRenderer|TextSnapshot|Timer|TimerEvent|Transform|true|TypeError|uint|URIError|URLLoader|URLLoaderDataFormat|URLRequest|URLRequestHeader|URLRequestMethod|URLStream|URLVariables|VerifyError|Video|width|XML|XMLDocument|XMLList|XMLNode|XMLNodeType|XMLSocket"; var AS3SystemObjects:String = "not_set_yet";
Next we will need to create the regular expressions to perform all the necessary searching, dont panic, I have done all the hard work for you, here they are.
var SingleQuoteString:RegExp = new RegExp("'.*'","g");
var DoubleQuoteString:RegExp = new RegExp("".*"","g");
var StartMultiLineQuote:RegExp = new RegExp("/\*.*","g")
var EndMultiLineQuote:RegExp = new RegExp("\*/.*","g")
var KeyWords:RegExp = new RegExp("(\b)(" + AS3KeyWords + "){1}(\.|(\s)+|;|,|\(|\)|\]|\[|\{|\}){1}","g");
var SystemObjects:RegExp = new RegExp("(\b)(" + AS3SystemObjects + "){1}(\.|(\s)+|;|,|\(|\)|\]|\[|\{|\}){1}","g");
var Comment:RegExp = new RegExp("//.*","g");
var Brackets:RegExp = new RegExp("(\{|\[|\(|\}|\]|\))","g");
Now it’s time to put all of this into action. I have created a function that will highlight the text in a text field called txtCode, here it is.
function HighlightSyntax(){
var InMultilineComment:Boolean = false;
for(var i=0;i<txtCode.numLines;i++){
if(InMultilineComment){
txtCode.setTextFormat(CommentFormat,txtCode.getLineOffset(i),txtCode.getLineOffset(i)+txtCode.getLineText(i).length);
InMultilineComment = !ParseExpression(EndMultiLineQuote,CommentFormat,i,false);
} else {
var CommentIndex:Number
ParseExpression(KeyWords,KeywordFormat,i,true);
ParseExpression(SystemObjects,SystemObjectFormat,i,true);
ParseExpression(Brackets,BracketFormat,i,false);
ParseExpression(SingleQuoteString,StringFormat,i,false);
ParseExpression(DoubleQuoteString,StringFormat,i,false);
CommentIndex = ParseExpression(Comment,CommentFormat,i,false,true);
InMultilineComment = ParseExpression(StartMultiLineQuote,CommentFormat,i,false,true);
if(InMultilineComment){InMultilineComment = !ParseExpression(EndMultiLineQuote,CommentFormat,i,false,true);}
}
}
}
function ParseExpression(exp:RegExp,format:TextFormat,lineno:Number,Trim:Boolean,DontSearchStrings:Boolean=false):Boolean{
var result:Object = exp.exec(txtCode.getLineText(lineno))
if (result == null) {return false};
while (result != null) {
if(DontSearchStrings){
var IsInString:Boolean = false;
if(InString(result,DoubleQuoteString,lineno) == true){IsInString = true}
if(InString(result,SingleQuoteString,lineno) == true){IsInString = true}
if(IsInString){return false}
}
if(Trim){
txtCode.setTextFormat(format,txtCode.getLineOffset(lineno) + result.index,txtCode.getLineOffset(lineno) + result.index+result[0].length - 1);
} else {
txtCode.setTextFormat(format,txtCode.getLineOffset(lineno) + result.index,txtCode.getLineOffset(lineno) + result.index+result[0].length);
}
result = exp.exec(txtCode.getLineText(lineno));
}
return true;
}
function InString(result:Object,exp:RegExp,lineno:Number):Boolean{
var stringResult:Object = exp.exec(txtCode.getLineText(lineno))
var IsInString:Boolean = false;
while (stringResult != null) {
if(result.index > stringResult.index && result.index < stringResult.index + stringResult[0].length){
IsInString = true;
}
stringResult = exp.exec(txtCode.getLineText(lineno))
}
return IsInString
}
Ok, I lied. It’s 3 functions. Let’s review things briefly:
I created the ParseExpression function to allow me to reuse a lot of code. It takes a RegExp a TextFormat and a line number, then applies the TextFormat to anything it finds in that line. result[0] is the exact match that was found, so we use result[0].length to determine the end index for the setTextFormat function. The DontSearchStrings parameter is to prevent highlighting keywords and comments in the middle of a string. If set to true then the InString function is used to determine it the keyword found is between quotation marks.
The only other part of the function that might need explaining is the InMultilineComment statement in the HighlightSyntax function. This is used to skip highlighting after a multi-line comment until it reaches the end of the comment.
Just give me the example files!
Ok, calm down, here is an example of all this in action. The only difference between the two examples is that example 2 shows you how to load a .txt or .as file into it.
Syntax Highlighting Example 1 Download
Syntax Highlighting Example 2 Download
Enjoy.
Summary
The introduction of regular expressions in AS3 allows us to perform syntax highlighting in real time, although this was possible in AS2, it was no where near as fast. We can also use RegExp to validate the content of TextFields for email addresses, phone numbers etc.
If anyone has a more complete list of AS3 keywords please let me know.
What Next?
This is a very basic example so there is much room for improvement. Here are a few things that I would like to see added:
- Scrollbars
- Syntax Highlighting as you type
- Lines between each function
- Collapsible functions
- Some kind of “Intellisense”
- The x in Colours to appear that colour 0×00FF00 for example would have a lime green x
If you would like to see anything added that is not already on this list, please feel free to add your comments below. I’m planning a follow up article that will show you how to replace pre elements with this AS3 highlighter at runtime using only javascript. Check back on the Atlas site for that
