I am currently working on syntax highlighting and code completion project, and making user control based on RichTextBox. I’ve had some problems adapting to the way RTB works and everything, but I have managed to make simple syntax highlighting.
Simple means that I highlight entire text every time user types a character. It’s not supposed to be fast or anything, but it is too slow. Performance issues become visible when I have about 500 chars worth of text, and I do only one pass through the text for each typed character(‘colorInterval’ function gets called about 100 times in one pass).
Performance analysis says the problem is TextRange constructor that takes about 80%+ of the time, and I use it every time I need to color an interval of text:
private void colorInterval(TextPointer start, TextPointer end)
{
TextRange range = new TextRange(start, end);
if(isFunction(range.Text)) colorAsFunction(range);
if(isInQuotes(range.Text)) colorAsQuoted(range);
...
}
So here goes my question:
Am I doing something wrong doing everything this way, or is there a way to boost performance of TextRange, recycle the ‘range’ object or something like that? What other solutions are there.
The simplest avenue is to (as you suggest) reuse the
TextRangeobject, if it really is the constructor that is taking up most of your time. TheTextRangepropertiesStartandEndare read only, but there is a public methodSelectwhich will update both, taking twoTextPointerobjects just like the constructor you have been using.(N.B. checking for a null reference before deciding whether to initialise the variable isn’t as neat as just instantiating a
TextRangein the declaration. Unfortunately,TextRangehas no public empty constructor andTextPointerno public constructors at all. You could create it with some dummy values in your class constructor to avoid this check.)Above, I said ‘if it really is the constructor’. Obviously, the profiling you’ve rightfully done has highlighted the constructor, but it could just as easily be a routine common to the constructor and the
Selectmethod.Assuming you don’t call
colorIntervalfrom more than one thread, I would say this is a better approach than you have currently whatever the time saving, because (I would guess that)colorIntervalis being called frequently and constant creation and garbage collection of the subsequentTextRangeobjects it leaves behind is certainly an inefficiency.Having made this suggestion, I strongly suggest you move away from the model where you scan the entire document every time you want to react to (for example) a single character change. Assuming you are targetting >= .net 3.5, the
RichTextBoxprovides aTextChangedevent that reports a list ofTextChangeobjects from which you can work out the location of (and characters added or removed by) changes.Naturally, there will be some work here because any change is unlikely to completely encapsulate a highlighted range. The
TextRangeclass has a method for finding the paragraphs in which the start and end of a range can be found, in case that helps. There’s probably a case for storing details of each highlighted range so you can quickly check for intersection.