Radii8Library/src/com/flexcapacitor/utils/SyntaxHighlighter.as (314 lines of code) (raw):

//////////////////////////////////////////////////////////////////////////////// // // Licensed to the Apache Software Foundation (ASF) under one or more // contributor license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright ownership. // The ASF licenses this file to You under the Apache License, Version 2.0 // (the "License"); you may not use this file except in compliance with // the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // //////////////////////////////////////////////////////////////////////////////// package com.flexcapacitor.utils { import flash.events.Event; import flash.events.EventDispatcher; import flash.events.TimerEvent; import flash.text.StyleSheet; import flash.utils.Timer; import mx.controls.TextArea; import mx.controls.textClasses.TextRange; import spark.components.TextArea; import flashx.textLayout.formats.TextLayoutFormat; import net.anirudh.as3syntaxhighlight.CodePrettyPrint; import net.anirudh.as3syntaxhighlight.PseudoThread; [Event(name="complete")] /** * Adds syntax highlighting to the text area. * * This class wraps around the AS3SyntaxHighlighter * and makes it easier to setup the text area. * * This class is incomplete it appears. * Some code may be from the example projects. * * http://code.google.com/p/as3syntaxhighlight/ * */ public class SyntaxHighlighter extends EventDispatcher { /** * spl = "var" or "function" token * str = string - ("something") * kwd = keyword * com = comment * typ = type * lit = literal * pun = punctuation - <,= * pln = plain * tag = tag - <(tag) * atn = attribute name * atv = attribute value - name="(value)" * src = source * dec = declaration * * * */ public static var CRIMSON_EDITOR_CSS:String = "" + ".spl {color: #4f94cd;} " + ".str {color: #800080;} " + ".kwd {color: #000088;} " + ".com {color: #008800;} " + ".typ {color: #0068CF;} " + ".lit {color: #006666;} " + ".pun {color: #1C02FF;} " + ".tag {color: #1C02FF;} " + ".pln {color: #222222;} " + ".atn {color: #660066;} " + ".atv {color: #880000;} " + ".dec {color: #660066;}"; public function SyntaxHighlighter(textarea:Object = null) { if (textarea is mx.controls.TextArea) { this.mxTextArea = mx.controls.TextArea(textarea); } else if (textarea is spark.components.TextArea) { this.sparkTextArea = spark.components.TextArea(textarea); } } //public var cssString:String =".spl {font-family:sandboxcode;color: #4f94cd;} .str { font-family:sandboxcode; color: #880000; } .kwd { font-family:sandboxcode; color: #000088; } .com { font-family:sandboxcode; color: #008800; } .typ { font-family:sandboxcode; color: #0068CF; } .lit { font-family:sandboxcode; color: #006666; } .pun { font-family:sandboxcode; color: #666600; } .pln { font-family:sandboxcode; color: #222222; } .tag { font-family:sandboxcode; color: #000088; } .atn { font-family:sandboxcode; color: #660066; } .atv { font-family:sandboxcode; color: #880000; } .dec { font-family:sandboxcode; color: #660066; } "; public var cssString:String = ".spl {color: #4f94cd;} .str {color: #880000; } .kwd {color: #000088;} .com {color: #008800;} .typ {color: #0068CF;} .lit {color: #006666;} .pun {color: #666600;} .pln {color: #222222;} .tag {color: #000088; } .atn {color: #660066;} .atv {color: #880000; } .dec {color: #660066; } "; private var codeStyle:StyleSheet; private var codePrettyPrint:CodePrettyPrint; private var codeTimer:Timer; private var asyncStop:Boolean; private var asyncRunning:Boolean; private var codeStylePF:StyleSheet; private var srclenPF:int; private var arrPF:Array; private var lenPF:int; private var firstNodePF:Boolean; private var firstIndexPF:int; private var pfasyncrunning:Boolean; private var pfasyncstop:Boolean; private var desclenPF:int; private var colorThread:PseudoThread; public var debug:Boolean; public var dispatchEvents:Boolean = true; /** * Interval in milliseconds to wait before. * Not sure this is working. * */ public var timerInterval:int = 200; /** * MX TextArea * */ [Bindable] public var mxTextArea:mx.controls.TextArea; /** * Spark TextArea * */ [Bindable] public var sparkTextArea:spark.components.TextArea; [Bindable] private var asyncCodeState:String; /** * Highlights the code in the text area * */ public function highlightCode():void { if (!codeTimer) { codeTimer = new Timer(timerInterval, 1); codeTimer.addEventListener(TimerEvent.TIMER, doPrettyPrint); } if (codeTimer.running) { codeTimer.stop(); } codeTimer.reset(); // wait for some time to see if we need to highlight or not codeTimer.start(); //trace("start highlighting gettimer=" + getTimer()); doPrettyPrint(); } /** * * */ private function doPrettyPrint(event:TimerEvent=null):void { //trace("start doPrettyPrint gettimer=" + getTimer()); if (!codeStyle) { codeStyle = new StyleSheet(); codePrettyPrint = new CodePrettyPrint(); codeStyle.parseCSS(cssString); } if (codePrettyPrint.asyncRunning) { codePrettyPrint.prettyPrintStopAsyc = true; if (mxTextArea) { mxTextArea.callLater(doPrettyPrint); } else if (sparkTextArea) { sparkTextArea.callLater(doPrettyPrint); } return; } if (pfasyncrunning) { pfasyncstop = true; if (mxTextArea) { mxTextArea.callLater(doPrettyPrint); } else if (sparkTextArea) { sparkTextArea.callLater(doPrettyPrint); } return; } //trace("start code in place gettimer=" + getTimer()); codeHighlightInPlace(); } private function pfinit(startIndex:int, endIndex:int):void { codeStylePF = codeStyle; srclenPF = endIndex - startIndex; arrPF = codePrettyPrint.mainDecorations; lenPF = arrPF.length; desclenPF = mxTextArea ? mxTextArea.text.length : sparkTextArea.text.length; firstNodePF = false; firstIndexPF = 0; pfasyncrunning = false; pfasyncstop = false; } private function processFormattedCodeAsync(startIndex:int, endIndex:int, completeFunction:Function, optIdx:int=0):Boolean { if (pfasyncstop) { pfasyncrunning = false; pfasyncstop = false; return false; } pfasyncrunning = true; if (arrPF==null || srclenPF<1) { pfasyncrunning = false; return false; } if (debug) trace("color worker " + optIdx); var tr:TextRange; var txtLayFmt:TextLayoutFormat; var thecolor:Object; var i:int = optIdx; if ( i > 0 && i % 5 == 0 ) { asyncCodeState = "Coloring (" + int((i / lenPF) * 100) + "%)..."; } var textLength:int; if (mxTextArea) { textLength = mxTextArea.text.length; } else if (sparkTextArea) { textLength = sparkTextArea.text.length; } if ( i < lenPF ) { /* find first node */ if ( arrPF[i] == 0 && firstNodePF == false ) { firstNodePF = true; return true; } else if ( arrPF[i] == 0 && firstNodePF == true ) { firstNodePF = false; firstIndexPF = i; } if ( i - 2 > 0 ) { if ( arrPF[i-2] != arrPF[i] && arrPF[i] < textLength ) { if (mxTextArea) { tr = new TextRange(mxTextArea, false, arrPF[i-2] + startIndex, arrPF[i] + startIndex); thecolor = codeStylePF.getStyle("." + arrPF[i-1]).color; // RangeError: Error #2006: The supplied index is out of bounds. // if we are not on the stage tr.color = thecolor; } else if (sparkTextArea) { //var txtLayFmt:TextLayoutFormat = sparkTextArea.getFormatOfRange(null, start, end); txtLayFmt = sparkTextArea.getFormatOfRange(null, arrPF[i-2] + startIndex, arrPF[i] + startIndex); txtLayFmt.color = codeStylePF.getStyle("." + arrPF[i-1]).color; sparkTextArea.setFormatOfRange(txtLayFmt, arrPF[i-2] + startIndex, arrPF[i] + startIndex); } } } return true; } if ( i > 0 ) { i -= 2; if ( arrPF[i] + startIndex < endIndex ) { if (mxTextArea) { tr = new TextRange(mxTextArea, false, arrPF[i] + startIndex, endIndex); thecolor = codeStylePF.getStyle("." + arrPF[i+1]).color; } else if (sparkTextArea) { txtLayFmt = sparkTextArea.getFormatOfRange(null, arrPF[i] + startIndex, endIndex); txtLayFmt.color = codeStylePF.getStyle("." + arrPF[i-1]).color; } var totalLength:int = mxTextArea ? mxTextArea.text.length : sparkTextArea.text.length; if ( totalLength >= endIndex ) { if (mxTextArea) { tr.color = thecolor; } else if (sparkTextArea) { sparkTextArea.setFormatOfRange(txtLayFmt, arrPF[i] + startIndex, endIndex); } } } } if ( completeFunction != null ) { completeFunction(); } if (debug) trace("color worker done"); pfasyncrunning = false; return false; } private function codePFComplete():void { asyncCodeState = ""; if (dispatchEvents) { dispatchEvent(new Event(Event.COMPLETE)); } } private function codeInPlaceComplete():void { asyncCodeState = "Coloring..."; if (dispatchEvents && hasEventListener(Event.OPEN)) { dispatchEvent(new Event(Event.OPEN)); } if (pfasyncrunning) { pfasyncstop = true; if (mxTextArea) { mxTextArea.callLater(codeInPlaceComplete); } else if (sparkTextArea) { sparkTextArea.callLater(codeInPlaceComplete); } return; } asyncRunning = false; if (mxTextArea) { pfinit(0, mxTextArea.text.length); colorThread = new PseudoThread(mxTextArea.systemManager, processFormattedCodeAsync, this, [0, mxTextArea.text.length, codePFComplete, 0], 3, 2); } else if (sparkTextArea) { pfinit(0, sparkTextArea.text.length); colorThread = new PseudoThread(sparkTextArea.systemManager, processFormattedCodeAsync, this, [0, sparkTextArea.text.length, codePFComplete, 0], 3, 2); } } private function lexInt(index:int, total:int):void { if ( index > 0 && index % 5 == 0 ) { asyncCodeState = "Lexing (" + int((index / total) * 100) + "%)..."; } } private function codeHighlightInPlace():void { asyncRunning = true; asyncCodeState = "Lexing..."; if (mxTextArea) { codePrettyPrint.prettyPrintAsync(mxTextArea.text, null, codeInPlaceComplete, lexInt, mxTextArea.systemManager); } else if (sparkTextArea) { codePrettyPrint.prettyPrintAsync(sparkTextArea.text, null, codeInPlaceComplete, lexInt, sparkTextArea.systemManager); } if (dispatchEvents) { dispatchEvent(new Event(Event.INIT)); } } } }