jacobslusser / ScintillaNET

A Windows Forms control, wrapper, and bindings for the Scintilla text editor.
MIT License
967 stars 245 forks source link

Multiple lexers inside StyleNeeded #433

Open f1nalspace opened 5 years ago

f1nalspace commented 5 years ago

I am trying to use multiple lexers inside StyleNeeded, but as soon as i use the second lexer the styles gets overriden from the previous one.

What i basically want is to call Cpp lexer, then call Html lexer for specific ranges, then do a custom lexer and lastly do Cpp lexer again for special ranges. Its the same process like doing PHP with HTML - but with more layers. Unfortunatly the Styles[] array seems to be persistent and the indices for each language always start at zero (Default) as well, meaning that Style.Cpp.Default is the same as Style.Html.Default.

Is there a way around that? Can i add a style offset of some sort - so it gets unique for each language?

Currently i have this, which only works for one lexer and one custom lexer :-(

Font editorFont = new Font(FontFamily.GenericMonospace, 14.0f, FontStyle.Regular);
editor.StyleResetDefault();
editor.Styles[Style.Default].Font = editorFont.Name;
editor.Styles[Style.Default].Size = (int)editorFont.SizeInPoints;
editor.StyleClearAll();

// Use container lexer for calling StyleNeeded
editor.Lexer = Lexer.Container;

editor.StyleNeeded += (s, e) =>
{
    Scintilla thisEditor = (Scintilla)s;
    EditorState editorState = (EditorState)thisEditor.Parent.Tag;

    int startPos = thisEditor.GetEndStyled();
    int line = thisEditor.LineFromPosition(startPos);
    startPos = thisEditor.Lines[line].Position;
    int endPos = e.Position;

    // Highlight C++
    thisEditor.Lexer = Lexer.Cpp;
    thisEditor.Styles[Style.Cpp.Default].ForeColor = Color.Black;  // This overwrites the default style :-(
    thisEditor.Styles[Style.Cpp.Identifier].ForeColor = Color.Blue;
    thisEditor.Styles[Style.Cpp.String].ForeColor = Color.Green;
    thisEditor.Colorize(startPos, endPos);

    // Find intersecting comment blocks
    Match[] commentMatches = GetCommentMatches(thisEditor, startPos, endPos);
    foreach (Match commentMatch in commentMatches)
    {
        int matchStart = commentMatch.Index;
        int matchEnd = matchStart + (commentMatch.Length - 1);

        // Highlight Html
        thisEditor.Lexer = Lexer.Html;
        thisEditor.Styles[Style.Html.Default].ForeColor = Color.Silver; // This overwrites the previous C++ default style :-(
        thisEditor.Styles[Style.Html.Tag].ForeColor = Color.Blue;
        thisEditor.Colorize(matchStart, matchEnd);

        // Highlight Doxygen
        thisEditor.Lexer = Lexer.Null;
        thisEditor.Styles[30].ForeColor = Color.Red; // 30 Seems to be a good index for a custom style
        thisEditor.Styles[31].ForeColor = Color.Yellow;
        CustomHighlightDoxygen(thisEditor, matchStart, matchEnd);
    }

    // Switch back to container lexer, so StyleNeeded is called again
    thisEditor.Lexer = Lexer.Container;
};

I dont want to write the lexers myself, because each lexer by itself works beutifully.

tobeypeters commented 5 years ago

You ever get this working? I'm also curious to see how your GetCommentMatches() works.

f1nalspace commented 5 years ago

You ever get this working? I'm also curious to see how your GetCommentMatches() works.

Initially i was using pure regex to detect the comments blocks, which may contain doxygen commands.

But now i am fully switched to a 100% custom lexing & parsing of C/C++/Doxygen/Html - which means i just use "Container" for everything and never use any integrating lexers nor styles.

Regarding your actual question, detecting the comment matches are easy - you need to find all comment blocks, on every change and save the regions in a list or something. GetCommentMatches() iterates from all blocks and does simple start/length lookup to search for intersections. Thats it.