Fast Colored TextBox for syntax highlighting

Introduction
For one of my projects I have felt the need of a text editor with syntax highlighting. At first I used a component inherited from RichTextBoх, but while using it for a large amount of text I found out that RichTextBoх highlights very slowly a large number of colored fragments (from 200 and more). When such highlighting has to be made in a dynamic way, it causes a serious problem.
Therefore I created my own text component which uses neither Windows TextBox nor RichTextBoх.
The rendering of a text was made completely only by the means of GDI+.
The component works fast enough with a large amount of text and also possesses tools to make comfortably dynamic syntax highlighting.
It has such settings as foreground color, font style, background color which can be adjusted for arbitrarily selected text symbols. One can easily gain access to a text with the use of regular expressions. WordWrap, Find/Replace, Code folding and multilevel Undo/Redo are supported as well.
Some properties and methods
FastColoredTextBox
class:
-
ClearStyle()
– Clears styles of the text -
CollapseBlock(int fromLine, int toLine)
,ExpandBlock(int fromLine, int toLine)
,ExpandBlock(int iLine)
- Collapse/Expand text block -
CollapseFoldingBlock(int iLine)
,ExpandFoldedBlock(int iLine)
- Collapse/Expand folding block -
Copy()
,Cut()
,Paste()
,ClearSelected()
,SelectAll()
– Standard text operations with selected text -
ClearUndo()
– Clears undo/redo stack -
DoCaretVisible()
– Scrolls control for display caret -
DoSelectionVisible()
- Scrolls control for display selection area -
- Style of folded blockFoldedBlockStyle
-
FoldingIndicatorColor
- Color of folding indicator (left vertical line between folding bounds) -
GetLine(int iLine)
– Return Range for given line index -
GetLineLength(int iLine)
– Returns chars count of given line index -
GetLineText(int iLine)
– Returns text for given line index -
GetRanges(string regexPattern)
,GetRanges(int fromLine, int toLine, string regexPattern)
– Returns Range of text for given regular expression. -
GoEnd()
,GoHome()
– Moves caret to first/last position. -
HighlightFoldingIndicator
- Enables folding indicator (left vertical line between folding bounds) -
IncreaseIndent()
,DecreaseIndent()
- Shifts selected block to left/right -
IndentBackColor
- Color of indent area -
InsertText(string text)
– Inserts text into current position. -
IsChanged
– Text was changed (user may set this property into false, after saving text into file) -
LeftIndent
- Left indent (in pixels) -
LinesCount
– Count of lines (without regard for wordwrap effect) -
Point
PlaceToPoint(Place place)
- Gets point for given line and char position -
int
PlaceToPosition(Place point)
- Gets absolute text position from line and char position -
Place
PointToPlace(Point point)
- Gets nearest line and char position from point -
int
PointToPosition(Point point)
- Gets nearest absolute text position for given point -
Place
PositionToPlace(int pos)
- Gets line and char position from absolute text position -
Undo()
,Redo()
,RedoEnabled
,UndoEnabled
– Standard undo/redo functions -
SelectedText
- Text of current selected range -
Selection
– Current selected range of text -
SelectionColor
– Background color for selection area. Alpha component of color must be less than 200. Otherwise alpha will be assigned to 50 forcedly. -
SelectionLength
,SelectionStart
– Standard (like RichTextBoх) properties of current selected text -
ServiceLinesColor
- Color of auxiliary lines -
ShowLineNumber
- Shows line number in left indent -
ShowFindDialog()
- Shows Find dialog form -
ShowReplaceDialog()
- Shows Replace dialog form -
Text
– All text (asstring
). This property includes Environment.NewLine chars as line separator -
TextVersion
- This counter is incremented each time changes the text -
WordWrap
– Enables/Disables WordWrap mode. -
WordWrapLinesCount
- Count of lines (include wordwrap effect) -
event
TextChanged
,TextChangedDelayed
- It occurs after insert, delete, clear, undo and redo operations -
event
VisibleRangeChanged
,VisibleRangeChangedDelayed
- It occurs after change of visible range of the text -
event
TextChanging
- It occurs before insert, delete, clear, undo and redo operations -
event
SelectionChanged
,SelectionChangedDelayed
- It occurs after change of selection
Range
class:
-
CharAfterStart
,CharBeforeStart
– char before/after start of selected area (include \n char) -
ClearStyle()
- Clears styles of range -
ClearFoldingMarkers()
- Clears folding markers of all lines of range -
Clone()
- Clones range -
Contains(Place place)
– Is range contains given char and line position? -
GetRanges(string regexPattern)
– Returns fragments of this Range object matched to given regular expression pattern. -
GoLeft()
,GoRight()
– Moves Start and End positions left/right (after this methods positions of Start and End will be equal). Returns false, if moving is impossible -
Normalize()
- Exchanges Start and End if End appears before Start -
Start
,End
– Start and End of range object. These properties are of type Place, which is represents line index and index of the character in the line -
SelectAll()
- Selects all chars of text and assign range to this object -
SetFoldingMarkers(string startFoldingPattern, string finishFoldingPattern)
- Sets folding markers -
SetStyle(Style style)
– Sets style for all chars of this Range object. -
SetStyle(Style style, string regexPattern)
– Sets style for fragments of this Range object matched to given regular expression. -
Text
– Returns text of this Range object
Implementation
For storage of characters of text, structure Char
is used:
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 using List<Char>
.
StyleIndex
is mask of styles indices, applied to this character. Each bit of StyleIndex
means that this symbol will be drawn by appropriate style. Because StyleIndex
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, was used the class Range
, 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;
}
Using the code
Syntax highlighting
Unlike RichTextBoх, the component does not use RTF. The information about the color and type of symbols is kept only in the component. 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 event TextChanged
. 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 method Range.
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+(?<range>[\w_]+?)\b");
The event handler TextChanged
utilized for coloring C#, VB and HTML syntax was implemented in demo application.
Apart from the event TextChanged
the events TextChanging
, VisibleRangeChanged
and SelectionChanged
may happen to be useful. The event TextChanging
appears before the text starts to be modified. The event SelectionChanged
occurs after the change of the cursor position in the component or while a selected fragment of text is being modified.
Code folding
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 method Range.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 style 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:
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 by ExpandBlock()
method.
Demo application contains sample for collapse all #region...#endregion
blocks 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 TextChanged
is triggered when you enter each character. And event TextChangedDelayed
work 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 use ExportToHTML
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.
Samples
Demo application has many samples. Below is a brief description:
-
Powerful sample. Contains many features: syntax highlighting, code folding, autocomplete, export, same words highlighting and other.
- Simplest syntax highlighting sample. Shows how to make simplest syntax highlighting.
- Marker sample. Shows how to make marker tool. Sample uses class
ShortcutStyle
for create clickable markers on text area:
-
Custom style sample. This example shows how to create own custom style. Next custom style draws frame around of the words:
class EllipseStyle : Style { public override void Draw(Graphics gr, Point position, Range range) { //get size of rectangle Size size = GetSizeOfRange(range); //create rectangle Rectangle rect = new Rectangle(position, size); //inflate it rect.Inflate(2, 2); //get rounded rectangle var path = GetRoundedRectangle(rect, 7); //draw rounded rectangle gr.DrawPath(Pens.Red, path); } }
- VisibleRangeChangedDelayed usage sample. This example shows how to highlight syntax for extremally large text by
VisibleRangeChangedDelayed
event.
- Simplest code folding sample. This example shows how to make simplest code folding.
- Autocomplete sample. This example shows how to create autocomplete functionality. Also implemented highlighting of same words:
- Brackets highlighting sample. This example shows how to highlight brackets.
- Joke sample :) Some additional features. Implemented custom
TextStyle
:
Performance
For storing one megabyte of text requires approximately 6 MB of RAM (include undo/redo stack objects). The coloring does not consume significant resources.
The use of regular expressions and saving memory usage, allow reach high performance component. I tested the file of 50,000 lines (about 1.6 MB) of C# code. The total time of insertion, and the syntax coloring was about 3 seconds. Further work with the text passed without significant delays.
Restrictions
The component does not support center or right alignment, automatical Drag&Drop and uses only monospaced fonts.
History
-
26 Feb 2011 - Added Find/Replace functionality, indent stuffs, showing of line numbers. Also added VB syntax highlighting.
-
28 Feb 2011 - Added code folding functionality (include current block highlighting). Added some features (caret blinking, increase/decrease indent of selected text). Optimized ReplaceAll functionality. Added HTML syntax highlighting. Increased general performance of control.
-
2 Mar 2011 - Style logic was revised. Added Html export. Added many samples. Added many, many features... :)
发表评论
k5PFVx I was recommended this web site by my cousin. I am not sure whether this post is written by him as no one else know such detailed about my trouble. You are wonderful! Thanks!
casino games online
casino real money
casino online
real money games
casino games onlineSE5EZE Thanks again for the article post.Thanks Again. Cool.
R1jNE0 There is visibly a lot to realize about this. I believe you made various nice points in features also.
It as not that I want to replicate your web-site, but I really like the design. Could you tell me which theme are you using? Or was it custom made?
U6Peqb Really informative blog article. Keep writing.
I blog frequently and I genuinely appreciate your content.
This article has truly peaked my interest.
I'm going to take a note of your blog and keep checking for new details about once per
week. I subscribed to your Feed too. fotballdrakter barn SheritaKi Bayern München Trikot Kinder
OdessaMar
CortezFar fotbollströjor MamieCorsf7aa1G I truly appreciate this blog post. Will read on
PURm4E We stumbled over here different website and thought I should check things
BGTcwl I went over this internet site and I believe you have a lot of fantastic information, saved to bookmarks (:.
SWQqts Thanks for sharing, this is a fantastic article.Really looking forward to read more. Will read on
Hello there,
My name is George, and I was wondering if you would like to have your website cyqdata.com promoted as a resource on my blog georgemartjr.com ?
We are updating our broken link resources to include up to date resources for our readers. Our resource links are manually approved as a do follow link.
If you are interested in having your site included as a resource on our blog, please let me know.
Thanks
GeorgeMAvPLQ Way cool! Some extremely valid points! I appreciate you penning this post plus the rest of the website is extremely good.
uRAxlc Very nice info and right to the point. I am not sure if this is really the best place to ask but do you people have any thoughts on where to hire some professional writers? Thank you
cKQRZN Say, you got a nice article post.Really thank you! Cool.
SWUK0X Valuable Website I have been checking out some of your stories and i can state pretty good stuff. I will surely bookmark your website.
pV4Aek You made some respectable factors there. I looked on the internet for the problem and located most individuals will associate with along with your website.
hENKKR I will right away clutch your rss as I can at to find your e-mail subscription link or e-newsletter service. Do you have any? Please permit me recognise so that I could subscribe. Thanks.
AzzBLK you have done a excellent task on this topic!
boBReq Really enjoyed this blog post.Really thank you! Really Great.
erNJSI Really enjoyed this blog post.Really looking forward to read more. Keep writing.
VBseKy not positioning this submit higher! Come on over and talk over with my website.
4rJ1z1 I value the blog article.Thanks Again. Fantastic.
5T5MQc Very good post. I certainly love this site. Keep it up!
Y2N4Zv You ave made some decent points there. I looked on the web to find out more about the issue and found most people will go along with your views on this site.
OXxTNz My searches seem total.. thanks. Is not it great once you get a very good submit? Great ideas you have here.. Enjoying the publish.. best wishes
Z4C9IL web owners and bloggers made good content as you did, the
cv749j Inspiring story there. What happened after? Take care!
ASnys4 This blog is no doubt cool additionally amusing. I have chosen a bunch of useful things out of this amazing blog. I ad love to return again and again. Cheers!
a0NERy Really informative article.Really thank you! Awesome.
I value the blog post.Really looking forward to read more.
uGnhNt Thank you ever so for you post.Really thank you! Awesome.
eOlhiw This blog is obviously awesome as well as informative. I have picked a bunch of handy advices out of this source. I ad love to return over and over again. Thanks a lot!
ak7zsE Really appreciate you sharing this post.Really thank you! Awesome.
z0OKA5
PJHiwD Whenever you hear the consensus of scientists agrees on something or other, reach for your wallet, because you are being had.
wddvya This is one awesome article.Thanks Again. Awesome.
8uDJwW Wow, awesome blog layout! How long have you been blogging for? you made blogging look easy. The overall look of your website is great, let alone the content!
BpfK3C This is one awesome blog post. Much obliged.
WYk5lU Looking forward to reading more. Great article post. Awesome.
LOGrkq Really enjoyed this blog.Thanks Again. Great.
PUCaNi Hello. impressive job. I did not expect this. This is a splendid story. Thanks!
kUaiB7 excellent post, very informative. I wonder why the other experts of this sector don't notice this. You must proceed your writing. I am confident, you have a great readers' base already!
MeJlW0 Muchos Gracias for your article post.Much thanks again. Fantastic.
41WQ0l Thank you for your article.Much thanks again.
LSYal2 I think this is a real great blog post.Really looking forward to read more. Awesome.
sBf51b Thanks again for the article.Thanks Again. Great.
Yuu99t Enjoyed every bit of your post.Really thank you! Cool.
WC1sLX Thank you for your article.Thanks Again. Awesome.