Fast Colored TextBox for Syntax Highlighting(用于语法高亮的快速着色TextBox)

原文地址链接:

http://www.codeproject.com/Articles/161871/Fast-Colored-TextBox-for-syntax-highlighting

源码、样例、帮助文档下载地址:

 http://download.csdn.net/detail/eyuanatqqdotcom/4667849

说明

在多个项目中,我都迫切地感觉到我需要一个代码高亮的文本编辑器。

最初,我使用了一个继承自RichTextBox的控件,但是随后我发现,在对大量文本数据的处理上,对大量片段(超过200个)进行着色,RichTextBox太慢了。

因为这种高亮方式是以动态的方式执行的,太带来了一个严重的问题。

因此我创建了自己的文本编辑器组建,它没有使用TextBox或者RichTextBox。

仅通过使用GDI+来实现。

该组建对于大量代码的着色速度非常快,而且也可以动态地对代码着色。 

可以对反复出现的文本符号设定背景色、字体、前景色等。

可以使用正则表达式、文本折叠、查找替换、代码这贴,多不撤销\重做。

实现

为了存储文本中的字符使用了字符结构体。

public struct Char
{
    public char c;
    public StyleIndex style;
}

The structure keeps the symbol (char, 2 bytes) and style index mask (StyleIndex, 2 bytes). Thus, on each character of text consumes 4 bytes of memory. Symbols are grouped into lines, which are implemented usingList.

StyleIndex is mask of styles indices, applied to this character. Each bit ofStyleIndex means that this symbol will be drawn by appropriate style. BecauseStyleIndex has 16 bits, control supports no more than 16 different styles.

Styles are stored in a separate list:

public readonly Style[] Styles = new Style[sizeof(ushort)*8];

In fact, Style is renderer of chars, backgrounds, borders and other design elements of the text.
Below is a typical implementation of one of the styles for rendering text characters:

public class TextStyle : Style
{
    public Brush ForeBrush { get; set; }
    public Brush BackgroundBrush { get; set; }
    public FontStyle FontStyle { get; set; }

    public override void Draw(Graphics gr, Point position, Range range)
    {
        //draw background
        if (BackgroundBrush != null)
            gr.FillRectangle(BackgroundBrush, position.X, position.Y, 
        (range.End.iChar - range.Start.iChar) * 
        range.tb.CharWidth, range.tb.CharHeight);
        //draw chars
        Font f = new Font(range.tb.Font, FontStyle);
        Line line = range.tb[range.Start.iLine];
        float dx = range.tb.CharWidth;
        float y = position.Y - 2f;
        float x = position.X - 2f;

        Brush foreBrush = this.ForeBrush ?? new SolidBrush(range.tb.ForeColor);

        for (int i = range.Start.iChar; i < range.End.iChar; i++)
        {
            //draw char
            gr.DrawString(line[i].c.ToString(), f, foreBrush, x, y);
            x += dx;
        }
    }
}

TextStyle contains foreground color, background color and font style of the text. When creating a new style, component checks style on its list, and if there is no style, it creates a new style, with its index.

You can create custom styles, inherited from Style class.

To work with fragments of text, the class Range was used, representing a continuous block of text, given the initial and final positions:

public class Range
{
    Place start;
    Place end;
}
public struct Place
{
    int iLine;
    int iChar;
}

使用该代码

语法高亮

不同于RickTextBox,该组建没有使用 RTF.

符号的颜色和样式仅存放于组件自身中。

 It means that the coloring of the component has to be redone every time when entering text. In this case, the event TextChanged is applied.

A Range object which contains the information about modified text range pass into the eventTextChanged. It permits the highlighting of the altered text fragment only.

For the search of fragments of text which need to be colored, it is possible to employ overloaded methodRange.SetStyle() which accepts search pattern (regular expression). For example, the following code can be used for the search and coloring of the comments of C# code (the part of the line starting from two forward slashes):

Style GreenStyle = new TextStyle(Brushes.Green, null, FontStyle.Italic);
...
private void fastColoredTextBox1_TextChanged(object sender, TextChangedEventArgs e)
{
    //clear style of changed range
    e.ChangedRange.ClearStyle(GreenStyle);
    //comment highlighting
    e.ChangedRange.SetStyle(GreenStyle, @"//.*$", RegexOptions.Multiline);
} 

Before beginning the coloring call, the method Range.ClearStyle() is used to clean out and delete the previous style.

The method SetStyle() highlights the text fragment corresponding to a regular expression. However, if the expression includes the named group "range", the group with a name "range" is highlighted. The name of the class which comes after the key words "class", "struct" and "enum" was bolded in the following example:

e.ChangedRange.SetStyle(BoldStyle, @"\b(class|struct|enum)\s+(?[\w_]+?)\b");

The event handler TextChanged utilized for coloring C#, VB, HTML and other languages syntax was implemented in demo application.

Apart from the event TextChanged the events TextChanging,VisibleRangeChanged and SelectionChanged may happen to be useful. The eventTextChanging appears before the text starts to be modified. The eventSelectionChanged occurs after the change of the cursor position in the component or while a selected fragment of text is being modified.

代码折叠

Control allows to hide blocks of text. To hide the selected text, use method CollapseBlock():

fastColoredTextBox1.CollapseBlock(fastColoredTextBox1.Selection.Start.iLine, 
                fastColoredTextBox1.Selection.End.iLine);
 

The result is shown in the picture:

The component supports automatic search for fragments of collapse (folding area). To set the pattern (Regex) to find the beginning and end of folding block, use methodRange.SetFoldingMarkers() in TextChanged handler.

For example, to search of blocks {..} and #region .. #endregion, use next handler:

private void fastColoredTextBox1_TextChanged(object sender, TextChangedEventArgs e)
{
    //clear folding markers of changed range
    e.ChangedRange.ClearFoldingMarkers();
    //set folding markers
    e.ChangedRange.SetFoldingMarkers("{", "}");
    e.ChangedRange.SetFoldingMarkers(@"#region\b", @"#endregion\b");
}
 

The result is shown in the picture:

Fast Colored TextBox for Syntax Highlighting(用于语法高亮的快速着色TextBox)_第1张图片

Folding blocks can be nested into each other.

Collapsed block can be opened by doubleclick on it, or click on marker '+'. Single click on folded area selects hidden block. Also, you can open hidden block programmatically byExpandBlock() method.

Demo application contains sample for collapse all #region...#endregionblocks of the text.

In addition to hiding the text, folding blocks help visually define the boundaries of the block where the caret is located. For this purpose, the left side of the control draws a vertical line (folding indicator). It shows the beginning and end of the current folding block, in which the caret is located.

Delayed Handlers

Many events (TextChanged, SelectionChanged, VisibleRangeChanged) have a pending version of the event. A deferred event is triggered after a certain time after the occurrence of major events.

What does this mean? If the user enters text quickly, then the TextChangedis triggered when you enter each character. And event TextChangedDelayedworks only after the user has stopped typing. And only once.

It is useful for lazy highlighting of large text.

Control supports next delayed events: TextChangedDelayed, SelectionChangedDelayed,VisibleRangeChangedDelayed. Properties DelayedEventsInterval and DelayedTextChangedInterval contain time of pending.

Export to HTML

Control has property Html. It returns HTML version of colored text. Also you can useExportToHTML class for more flexibility of export to HTML. You can use export to HTML for printing of the text, or for coloring of the code of your web-site.

Clipboard

Control copies the text in two formats - Plain text and HTML.
If the target application supports inserting HTML (e.g. Microsoft Word) then will be inserted colored text. Otherwise (such as Notepad) will be inserted plain text.

Hotkeys

Control supports next hotkeys:

  • Left, Right, Up, Down, Home, End, PageUp, PageDown - moves caret
  • Shift+(Left, Right, Up, Down, Home, End, PageUp, PageDown) - moves caret with selection
  • Ctrl+F, Ctrl+H - shows Find and Replace dialogs
  • F3 - find next
  • Ctrl+G - shows GoTo dialog
  • Ctrl+(C, V, X) - standard clipboard operations
  • Ctrl+A - selects all text
  • Ctrl+Z, Alt+Backspace, Ctrl+R - Undo/Redo opertions
  • Tab, Shift+Tab - increase/decrease left indent of selected range
  • Ctrl+Home, Ctrl+End - go to first/last char of the text
  • Shift+Ctrl+Home, Shift+Ctrl+End - go to first/last char of the text with selection
  • Ctrl+Left, Ctrl+Right - go word left/right
  • Shift+Ctrl+Left, Shift+Ctrl+Right - go word left/right with selection
  • Ctrl+-, Shift+Ctrl+- - backward/forward navigation
  • Ctrl+U, Shift+Ctrl+U - converts selected text to upper/lower case
  • Ctrl+Shift+C - inserts/removes comment prefix in selected lines
  • Ins - switches between Insert Mode and Overwrite Mode
  • Ctrl+Backspace, Ctrl+Del - remove word left/right
  • Alt+Mouse, Alt+Shift+(Up, Down, Right, Left) - enables column selection mode
  • Alt+Up, Alt+Down - moves selected lines up/down
  • Shift+Del - removes current line

Brackets Highlighting

Control has built-in brackets highlighting. Simply set properties LeftBracketand RightBracket. If you want to disable brackets highlighting, set it to '\x0'. For adjust color of highlighting, use propertyBracketsStyle. For adjusting of time of pending of highlighting, changeDelayedEventsInterval.

Interactive Styles

You can create own interactive(clickable) styles. To do this, derive your class from theStyle, and call AddVisualMarker() from your overridden Draw() method. To handle a click on the marker, use eventsFastColoredTextBox.VisualMarkerClickor Style.VisualMarkerClick or override method Style.OnVisualMarkerClick().
Also you can use built-in style ShortcutStyle. This class draws little clickable rectangle under last char of range.

Styles Priority

Each char can contain up to 16 different styles. Therefore, the matter in which order these styles will be drawn. To explicitly specify the order of drawing, use the methodFastColoredTextBox.AddStyle():

fastColoredTextBox1.AddStyle(MyUndermostStyle);
fastColoredTextBox1.AddStyle(MyUpperStyle);
fastColoredTextBox1.AddStyle(MyTopmostStyle);


 

This methods must be called before any calls of Range.SetStyle(). Otherwise, the draw order will be determined by the order of calls of methodsRange.SetStyle().

Note: By default, control draws only one TextStyle(or inherited) style - undermost from all. However, you can enable the drawing of the symbol in manyTextStyle, using the property FastColoredTextBox.AllowSeveralTextStyleDrawing. This applies only toTextStyle(or inherited) styles, other styles(inherited from Style) are drawn in any case.

If char has not any TextStyle, it will drawing by FastColoredTextBox.DefaultStyle. DefaultStyle draws over all other styles.

Call method ClearStyleBuffer() if you need reset order of drawing.

Also, to adjust the look of text, you can apply a semitransparent color in your styles.

Built-in highlighter

FastColoredTextBox has built-in syntax highlighter for languages: C#, VB, HTML, SQL, PHP, JS.
Note: You can create own syntax highlighter for any language.

Property HighlightingRangeType specifies which part of the text will be highlighted as you type (by built-in highlighter). ValueChangedRange provides better performance. Values VisibleRange andAllTextRange - provides a more accurate highlighting (including multiline comments), but with the loss of performance.

Multiline Comments Highlighting

If your custom language supports multiline comments or other multiline operators, you may encounter a problem highlighting such operators. PropertyChangedRange from TextChanged event contains only changed range of the text. But multiline comments is larger thanChangedRange, from what follows incorrect highlighting. You can solve this problem in the following way: InTextChanged handler use VisibleRange or Range property of FastColoredTextBox instead of ChangedRange. For example:

private void fastColoredTextBox1_TextChanged(object sender, TextChangedEventArgs e)
{
    //old edition
    //Range range = e.ChangedRange;

    //new edition
    Range range = (sender as FastColoredTextBox).VisibleRange;//or (sender as 
                        //FastColoredTextBox).Range

    //clear style of changed range
    range.ClearStyle(GreenStyle);
    //comment highlighting
    range.SetStyle(GreenStyle, @"//.*$", RegexOptions.Multiline);
    range.SetStyle(GreenStyle, @"(/\*.*?\*/)|(/\*.*)", RegexOptions.Singleline);
    range.SetStyle(GreenStyle, @"(/\*.*?\*/)|(.*\*/)", RegexOptions.Singleline | 
                RegexOptions.RightToLeft);
}


 

Note: Using VisibleRange instead of ChangedRangedecreases performance of the control.
Note: If you use built-in highlighter, you can adjust property HighlightingRangeType.

Using XML Syntax Descriptors

Component can use the XML file for syntax highlighting. File name specified in the propertyDescriptionFile.

For highlighting, you need to set the Language property to Custom.

The file may contain information about styles, rules of syntax highlighting, folding parameters and brackets.
Below is a list of valid tags and attributes to them:

  • ... - root XML node.
  • - sets the brackets for highlighting