سؤال

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 TextRange object, if it really is the constructor that is taking up most of your time. The TextRange properties Start and End are read only, but there is a public method Select which will update both, taking two TextPointer objects just like the constructor you have been using.

protected TextRange range;

private void colorInterval(TextPointer start, TextPointer end)
{
  if (range == null)
    range = new TextRange(start, end);
  else
    range.Select(start, end);
  ...
}

(N.B. checking for a null reference before deciding whether to initialise the variable isn't as neat as just instantiating a TextRange in the declaration. Unfortunately, TextRange has no public empty constructor and TextPointer no 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 Select method.

Assuming you don't call colorInterval from 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) colorInterval is being called frequently and constant creation and garbage collection of the subsequent TextRange objects 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 RichTextBox provides a TextChanged event that reports a list of TextChange objects 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 TextRange class 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.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top