简体   繁体   中英

Syntax highlighting richtextbox in C# on a single line only

I'm working on my own syntax highlighter using a Richtextbox. It's already working, but I've noticed that the typing slows down a lot when there's to many lines of code. This is because my syntax highlight function is coloring all the words in the entire Richtextbox on every change made to it. Here's a minimal example of the function to see how it works:

private void colorCode()
{
    // getting keywords/functions
    string keywords = @"\b(class|function)\b";
    MatchCollection keywordMatches = Regex.Matches(codeBox.Text, keywords);

    // saving the original caret position + forecolor
    int originalIndex = codeBox.SelectionStart;
    int originalLength = codeBox.SelectionLength;
    Color originalColor = Color.Black

    // focuses a label before highlighting (avoids blinking)
    titleLabel.Focus();;

    // removes any previous highlighting (so modified words won't remain highlighted)
    codeBox.SelectionStart = 0;
    codeBox.SelectionLength = codeBox.Text.Length;
    codeBox.SelectionColor = originalColor;

    foreach (Match m in keywordMatches)
    {
        codeBox.SelectionStart = m.Index;
        codeBox.SelectionLength = m.Length;
        codeBox.SelectionColor = Color.Blue;
    }

    // restoring the original colors, for further writing
    codeBox.SelectionStart = originalIndex;
    codeBox.SelectionLength = originalLength;
    codeBox.SelectionColor = originalColor;

    // giving back the focus
    codeBox.Focus();
}

To solve the problem, I want to write a function that doesn't change the entire Richtextbox, but just the line of the cursor position instead. I realise this will still cause the same issue on minified code, but that's not a problem for me. The problem is, I can't seem to get it working. This is what I've got so far:

void changeLine(RichTextBox RTB, int line, Color clr, int curPos){
    string testWords = @"\b(test1|test2)\b";
    MatchCollection testwordMatches = Regex.Matches(RTB.Lines[line], testWords);

    foreach (Match m in testwordMatches)
    {
        //RTB.SelectionStart = m.Index;
        //RTB.SelectionLength = m.Length;
        RTB.SelectionColor = Color.Blue;
    }

    RTB.SelectionStart = curPos;
    RTB.SelectionColor = Color.Black;
}

The problem is that it does the coloring when a word in testWords is found, but it colors the entire line instead of just the word. This is because I can't figure out a way of doing the selections right. So I'm hoping you guys can help me out with this.

Edit:

I'd like to add that I did thought about other solutions, like putting the lines in a List, or using a Stringbuilder. But those will turn the lines into strings and don't allow me to do color formatting like the Richtextbox does.

Well, you obviously need language lexer and parser. This task is not solvable by using Regex. It's just doesn't capable to accomplish this because of some fundamental grammar rules (or "power levels" of grammars) (read about Thomsky hierarchy of grammars).

What you need is to use some grammar toolkit. For example ANTLR4 provide grammar lexer/parser generator and set of already predefined grammars.

For example, you can find a lot of user-written grammars in here (including latest C# syntax): https://github.com/antlr/grammars-v4

Then just generate parser/lexer by it and feed it your string. It will output full hierarchy with indexes and lengths of each token, and you can colorize them without jumping across entire rich box.

Also, consider to use some timeout between user input, so you don't colorize your output every symbol (just save color from previous token, and use it for some time, until you recolorize output, then refresh). This way it will go as smoothly as it is in Visual Studio.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM