7

I have a RichTextBox and once the user loads a file, my program proceeds to scan the entire file in order to change the color of certain words. Here is my code:

static Regex cKeyWords = new Regex(@"\b(?=[a-gilr-w])(?:
     s(?:hort|i(?:gned|zeof)|t(?:atic|ruct)|witch) | c(?:ase|har|on(?:st|tinue)) |
     e(?:lse|num|xtern) | i(?:f|nt) | f(?:loat|or) | d(?:o|efault|ouble) | un(?:ion|signed) |
     re(?:gister|turn) | vo(?:id|latile) | while | break | long | typedef | auto | goto
     )\b",
     RegexOptions.Compiled | RegexOptions.IgnorePatternWhitespace);

...

programTextBox.Enabled = false;
int selectStart = this.programTextBox.SelectionStart;
programTextBox.SuspendLayout();
MatchCollection matches = cKeyWords.Matches(programTextBox.Text);
foreach (Match match in matches)
{
    if (match.Index == 0)
        programTextBox.Select(match.Index, match.Length/* - 1*/);
    else
        programTextBox.Select(match.Index + 1, match.Length - 1);
    programTextBox.SelectionColor = Color.Blue;
}
programTextBox.Select(selectStart, 0);
programTextBox.SelectionColor = Color.Black;
programTextBox.Enabled = true;
programTextBox.ResumeLayout();

Problem: my code takes about 5 and a half seconds to scan and change the colors of all the keywords in a file that has 200,000 characters.

I've been told before that I shouldn't use a Regex for that kind of stuff, but after doing several tests I figured out that the: MatchCollection matches = cKeyWords.Matches(programTextBox.Text);

only takes about 0.1s and removing the

programTextBox.SelectionColor = Color.Blue;

reduces the total execution time of my code from 5.5s to about 0.3s

How? Why? And most importantly: What can I do?

ryanyuyu
  • 6,366
  • 10
  • 48
  • 53
OC_
  • 446
  • 6
  • 19
  • Oh yeah, forgot to mention that. Yes, it is. – OC_ May 11 '15 at 20:05
  • 1
    RichTextBox is not particularly efficient.. You could try applying the syntax highlighting by building the RTF yourself: [rtf spec](https://www.microsoft.com/en-nz/download/details.aspx?id=10725). Assign the result to the [`Rtf`](https://msdn.microsoft.com/en-us/library/system.windows.forms.richtextbox.rtf.aspx) property of the textbox. – Blorgbeard May 11 '15 at 20:05
  • 5
    Suspend painting before the loop and resume it immediately after it. http://stackoverflow.com/questions/3282384/richtextbox-syntax-highlighting-in-real-time-disabling-the-repaint or better find a control which supports syntax highlighting. There are several open source libraries available. – Sriram Sakthivel May 11 '15 at 20:06
  • I'm not sure, but maybe batching your work between calls to `SuspendLayout` and `ResumeLayout` might help. – Drew Noakes May 11 '15 at 20:07
  • Actually, I'm already doing this. Check the edited post and sorry for forgetting it! – OC_ May 11 '15 at 20:08
  • @DrewNoakes `SuspendLayout` and `ResumeLayout` makes no sense in richtextbox. It is helpful only in container controls. – Sriram Sakthivel May 11 '15 at 20:09
  • 1
    See [How to append text to RichTextBox without scrolling and losing selection?](http://stackoverflow.com/a/6550415/719186). – LarsTech May 11 '15 at 20:22
  • @LarsTech I took a quick look at the link you posted and it seems to be about screen flickering when updating the richtextbox. However, my problem is not screen flickering (because there isn't): it's about the time it takes to apply the changes to the text. – OC_ May 11 '15 at 20:29
  • @OC_ By suspending the drawing to make your highlight changes, you are saving a lot cycles. Try it. – LarsTech May 11 '15 at 20:35
  • 2
    Suspend layout and after u've done changes Resume like @SriramSakthivel suggested and then call RichTextbox.Refresh(). This will paint only visible items. I did it for my datagridview project, saves a lot of time. – Luntri May 12 '15 at 10:19
  • I actually tried adding Suspend() and Resume() (Drawing/Painting) as extension methods for my RichTextBox, called them and also added Refresh() after Suspend(), but it doesn't seem to work any faster... – OC_ May 13 '15 at 19:24

3 Answers3

1

I maintain the C++ syntax highlighter for VS Code, which has single regex patterns that are more than 9000 character's long. The regex you wrote is very efficient, probably too efficient (removing the lookahead would help more than breaking up the words).

This is a fundamental UI issue. The UI is slow, there is no way for something like VS Code to highlight 200,000 lines in under a second. And that's fine because the user is never going to be looking at 200,000 lines at one time. So instead Atom/VS Code intelligently only call the equivalent programTextBox.SelectionColor = Color.Blue; for the lines that are visible. Same with Sublime and other text editors; they only update what-you-see and what-was-changed, because it is very expensive to mess with the GUI.

Instead of updating the UI ~100,000 times, do what Sriram Sakthivel's comment says (not sure why he didn't make his an answer) "Suspend painting before the loop and resume it immediately after it. RichTextBox syntax highlighting in real time--Disabling the repaint or better yet find a control which supports syntax highlighting. There are several open source libraries available."

This is effectively the same solution that is used for every text editor.

Jeff Hykin
  • 1,846
  • 16
  • 25
0

Have you tried this?

This blocks painting and actually seems to block it properly. I only had a small test file to put through it, but it seemed to work pretty well.

Community
  • 1
  • 1
BugFinder
  • 17,474
  • 4
  • 36
  • 51
0

Instead of doing syntax highlighting yourself, try using ICSharpCode.TextEditor, the syntax-highlighting editor for the SharpDevelop IDE.

plushpuffin
  • 191
  • 4