Initially I wanted to be able to easily highlight syntax on my website while writing things. I took a look at Microsoft's QuickStart highlighter, and even based my first implementation on it. But I was quite disappointed about the endresult: it didn't really give me what I was looking for.

So I started with my own implementation from scratch. My requirements were:

  • Extensibility - it should be easy to extend existing highlighters.
  • Reusability - it should be easy to reuse parts of the highlighters.
  • Configuration support - highlighters must be able to be build from a configuration file and/or store their configuration.
  • Separated functionality - a highlighter shouldn't for example parse the syntax in HTML.
  • Easy to use - the client of the component should be able to parse code for a specific language with just a few lines of code.
  • ASP.NET support - it should be possible to easily highlight code on a page, either in-line or code-only.

I started modelling the things I had in mind and finally came up with the following model:

The Class Diagram of the Highlighter

In this model you can see that an highlighter works like a facade: the client doesn't have to worry about setting up the right scanners the right way. It's all done in each highlighter. This means that the requirement 'easy to use' is met in this model.
Each highlighter contains a set of scanners, which together basically form an highlighter. Scanners can be written once and be used within all the highlighters. Because I've implemented a chain of responsibility for the relationship between the scanners, highlighters or clients of highlighters can easily modify the chain. All this meets the requirements 'reusability', 'separated functionality' and 'extensibility'.
Since highlighters don't actually parse the syntax, but parser implementations do, requirement 'separated functionality' is met again. At any time someone could add a parser and still use all the highlighters.

After building this model, I started with the implementation of the design. When the core was done, I started implementing a set of common scanners, such as an XmlScanner, and finally implemented some highlighters, such as a CSharpHighlighter for highlighting C# code, and an HTML parser.

The requirement 'ASP.NET support' was not met yet at this point. So I made a ASP.NET server control which basically acted as a Label server control, except that it had 3 modes: all text, text and in-line source, and all source.

To meet the last requirement I extended the interface for scanners. With this extended interface, scanners could both load and save their configuration. By providing an implementation configuration manager which makes use of this extension, the last requirement was met too.

License:
This project is released under Ms-PL. Please contact me directly for questions or exceptions.

Downloads:

Recent updates (May 22nd, 07):

  • Fixed: CSS failed to highlight when plain text was used. Credits go to Andrew Powell for finding and fixing this.
  • Updated: Incorporated changes provided by Atif Aziz to take away a major performance bottleneck.
  • Fixed: XML attributes which contain double quotes and single quotes are now properly processed.
Hi,

Do you use it in your website ?
How ?

Thanks
Yes, I use this for both my posts and the pastecode service. Please have a look at the SourceHighlighter for displaying code on a page. It supports different modes, such as Inline (which is useful for forums/blogs and only highlights code between [ code=language][/code] tags) or Literal (code only).
Hi,
is it possible to change the font size in this code highliter?

Jan
If you use the highlighter within your own page, you can define the font-size through a stylesheet. Simply change the font-style of it's container.

It's not possible to change the font-size if you use the handler, unless you modify the code of the handler.
Hello is it possible to remove the property "nowrap" from your control. In some cases the nowrap property is not useful.

Jan
May I re-use this code? Under what licence have you released the source code?
You can use the source code and binaries as you wish.
Java:
1 

system.console.write("hello, highlighter!");


:D
Hi!

I downloaded and I'm using your great tool.

I modified somethings in SQLHighlighter.cs and some other files. The main modification was the keywords, functions, operators, etc... to accept SQL Server 2005 schema.

It's very nice if you could update this "settings". I can send you the code parts that was modified.
I forgot to ask a qustion...
How to "colorize" the operators, separators? (like "(", ".", "+", "'", etc..)
SQL:
1 
2 
3 
4 

Select CollectionID, CollectionName, CollectionInfo
From Collection
Where CollectionID Like 'DSE0%'
Order By CollectionID

Nice job! I will use it. But I have to modify it to work with .NET 1.1. A-a-ui :-(

<?xml version="1.0" encoding="UTF-8"?>
<root>
<somechild att="text">once more text</somechild>
</root>
now with highlightning

XML:
1 
2 
3 
4 

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <somechild att="text">once more text</somechild>
</root>

I must say that i admire you work with this.
I just can't find wich technics and languages it does support?
Nice work.
An enum would be nice on the "Language" property of the control so that one doesn't have to remember which strings activate each highlighter
string str;
string str1;
is there someone ho have a example how to use the
<CodeLayoutTemplate></CodeLayoutTemplate>
<LineNumberTemplate></LineNumberTemplate>
<SourceLineTemplate></SourceLineTemplate>

templates

thank in advance

/mikkel
thanks
Hey, Could you please let me know exactly how can I use it with Asp.net? Do you have an example with Asp.net implemented with cool tool?

THanks

Joesy
I get the following error when using your sample project.

"There is no parser set for this highlighter."
I like this code. It's fast and it's extensible.
This looks like it will be very useful. I am looking for a client-side Javascript solution. Could it be possible for me to do this in Javascript? If not then screw it I will just use this.
some implemention is based on javascript, so it can work offline.
select * from me
Pascal:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 

function IsAdmin: Boolean;
const
  SECURITY_NT_AUTHORITY: TSIDIdentifierAuthority =
    (Value: (0, 0, 0, 0, 0, 5));
  SECURITY_BUILTIN_DOMAIN_RID = $00000020;
  DOMAIN_ALIAS_RID_ADMINS = $00000220;

var
  hAccessToken: THandle;
  ptgGroups: PTokenGroups;
  dwInfoBufferSize: DWORD;
  psidAdministrators: PSID;
  x: Integer;
  bSuccess: BOOL;
begin
  Result   := False;
  bSuccess := OpenThreadToken(GetCurrentThread, TOKEN_QUERY, True, hAccessToken);
  if not bSuccess then
  begin
    if GetLastError = ERROR_NO_TOKEN then
      bSuccess := OpenProcessToken(GetCurrentProcess, TOKEN_QUERY, hAccessToken);
  end;
  if bSuccess then
  begin
    GetMem(ptgGroups, 1024);
    bSuccess := GetTokenInformation(hAccessToken, TokenGroups, ptgGroups, 1024, dwInfoBufferSize);
    CloseHandle(hAccessToken);
    if bSuccess then
    begin
      AllocateAndInitializeSid(SECURITY_NT_AUTHORITY, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, psidAdministrators);
      {$R-}
      for x := 0 to ptgGroups.GroupCount - 1 do
        if EqualSid(psidAdministrators, ptgGroups.Groups[x].Sid) then
        begin
          Result := True;
          Break;
        end;
      {$R+}
      FreeSid(psidAdministrators);
    end;
    FreeMem(ptgGroups);
  end;
end;

This is an excellent little tool to put on a website!
Have you any brief instructions on the best method of implementing this?
Wilco,
I think there is a serious bug in windows forms.
After pasting lots of code in the textbox, and I start editing the code somewhere in the middle of the visible text, this will cause the textbox to scroll the line that i'm editing to the bottom of the RichTextboxEx control(including the cursor). So i end up editing that line at the very bottom of the control. Thus, every time I put the cursor somewhere in the middle and hit a key, the text gets scrolled so that the line is at the bottom of the box. This is quite anoying. I suppose it has to do with repainting the control after each keystroke. The normal behavior would be to NOT scroll the text at all, no matter where the cursor is editing text in the visible area of the control.
This behavior is not visible when there is less text than the height of the control.
Also, the behavior is not visible when the box is scrolled at the very top.
From what i see now, it seems that the box tends to scroll upwards. Also, this behavior, when typing, is accompanied by quite a bit of flicker.
Felix: The thing that is included in the download is merely a prototype I included long ago for no great reasons. I've never really bothered creating a proper implementation, because I personally didn't have a use for it. If you want to use the highlighter in a winforms app, I would recommend creating your own control on top of it yourself.
Wilco, awesome stuff!

We're looking for a highlighter to be included in our open source ASP.NET forum/support system HnD (http://www.llblgen.com/hnd) and as the forum parses the UBB text to xml fragments which are then migrated to html snippets which are then stored in the db, we can't use an asp.net control inside the page to do the highlighting. A component which simply spits out the HTML based on the input text is ideal! :)

I've to study the license terms though, as the forum is GPLed.

C#:
1 
2 
3 
4 
5 
6 

public string Getaa()
{
  string aa;
  aa = "dfdf";
  return aa;
}

Public string Getaa()
{
string aa;
aa = "dfdf";
return aa;
}
C#:
1 
2 
3 
4 
5 
6 

Public string Getaa()
{
string aa;
aa = "dfdf";
return aa;
}

Frans: Feel free to contact me offline if you have any concerns about the license. I am very open to what people want to do with this library, both in terms of usage as well as in terms of deployment.
Thanks for this great software :-)

I am using it at http://beta.guidetocsharp.de ... :-)
I keep getting this error whenever I type. How come?
BTW, I set WinForms tester as the default startup.

if (this.parser == null)
throw new Exception("There is no parser set for this highlighter.");
PHP:
1 
2 
3 
4 

echo 'test';
<?php
echo 'test';
?>

Great Tool!
However, I am using it with BlogEngine.NET and SyntaxHighlightExtension that could be found here. Everything works well unless I colorize C++, which colorizes "string" and does not colorize everyting between /* ... */. Sample colorization can be found at http://amer.ath.cx/page/TinyPascal.aspx. It seems that it colorizes similar to C#, but not completly.

Thanks in advance
For those above who can't get the WinFormsTester project to stop crashing with a default extraction, change line four of the InitializeComponent method inside MainForm.cs to:

this.highlighter = new Wilco.SyntaxHighlighting.CSharpHighlighter(new Wilco.Windows.SyntaxHighlighting.RtfParser(outputBox));
C#:
1 
2 
3 
4 
5 
6 

private bool blnThisIsGood = true;
private bool blnImHappy;
if (blnThisIsGood)
{
blnImHappy = true;
}



Thank's for this, I'll start playing with it right now.
Just another test...
C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 

static public string GetStringRegistryValue(string key, string defaultValue)
		{
			RegistryKey rkCompany;
			RegistryKey rkApplication;

			rkCompany = Registry.CurrentUser.OpenSubKey(SOFTWARE_KEY, false).OpenSubKey(COMPANY_NAME, false);
			if( rkCompany != null )
			{
				rkApplication = rkCompany.OpenSubKey(APPLICATION_NAME, true);
				if( rkApplication != null )
				{
					foreach(string sKey in rkApplication.GetValueNames())
					{
						if( sKey == key )
						{
							return (string)rkApplication.GetValue(sKey);
						}
					}
				}
			}
			return defaultValue;
		}

<form:button form:name="PushButton" form:control-implementation="ooo:com.sun.star.form.component.CommandButton" form:id="control6" form:label="Persist" form:button-type="submit" office:target-frame="" xlink:href="" form:image-data="" form:xforms-submission="persistData" form:delay-for-repeat="PT0.50S" form:image-position="center">
<form:properties>
<form:property form:property-name="DefaultControl" office:value-type="string" office:string-value="com.sun.star.form.control.CommandButton" />
</form:properties>
</form:button>
XML:
1 
2 
3 
4 
5 

<form:button form:name="PushButton" form:control-implementation="ooo:com.sun.star.form.component.CommandButton" form:id="control6" form:label="Persist" form:button-type="submit" office:target-frame="" xlink:href="" form:image-data="" form:xforms-submission="persistData" form:delay-for-repeat="PT0.50S" form:image-position="center">
<form:properties>
<form:property form:property-name="DefaultControl" office:value-type="string" office:string-value="com.sun.star.form.control.CommandButton" />
</form:properties>
</form:button>

XML:
1 
2 
3 
4 
5 

<form:button>
  <form:properties>
    <form:property form:property-name="DefaultControl" />
  </form:properties>
</form:button>

Quick question: will this work as an extension to BlogEngine.Net?

:-)

/Jesper
<form:button>
<form:properties>
<form:property form:property-name="DefaultControl" />
</form:properties>
</form:button>

SQL:
1 
2 
3 
4 

Select CollectionID, CollectionName, CollectionInfo
From Collection
Where CollectionID Like 'DSE0%'
Order By CollectionID

Hi,

I notices that syntax highlighting crushes at my website. I am using BlogEngine.NET extension and occasionally the application breaks at the parser. I also noticed that your website was down for couple of days ... is there a possible memory leak?

Thanks in advance!
Amer
Hi.
Excelent work. I am using this Highlighter and works fine.
Just a question: cant I do a <Undo> (Ctrl+Z) at a deleted text?
Thanks.
Wilsade
Just a Teste


C#:
1 
2 
3 

Public Function(ByVal teste as String) As Object
Response.Write("Teste")
End Function

Just a Test


C#:
1 
2 
3 

Public Function(ByVal teste As String) As Object
Response.Write("Teste")
End Function

Hey, very cool Tool!
I just dont figure out how to use :D
But i will try harder :D

A Final Test:

Int32 i = 1;
String a =""
while(i<=100){
a= request.Querystring["id"];
i++;
}
Response.Write("GREAT TOOL!");
Wonderful! Looks good :) Some random test of C# and SQL

C#:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

namespace Wilco.Windows.SyntaxHighlighting
{
	/// <summary>
	/// Summary description for Form1.
	/// </summary>
	public class Form1 : System.Windows.Forms.Form
	{
		private RichTextBoxEx outputBox;
		private Wilco.SyntaxHighlighting.HighlighterBase highlighter;
		private ListBox intelliMenu;

		/// <summary>
		/// Required designer variable.
		/// </summary>
		private System.ComponentModel.Container components = null;

		public Form1()
		{
			//
			// Required for Windows Form Designer support
			//
			InitializeComponent();

			//
			// TODO: Add any constructor code after InitializeComponent call
			//
		}

		/// <summary>
		/// Clean up any resources being used.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}



SQL:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 
28 
29 
30 
31 
32 
33 
34 
35 
36 
37 
38 
39 
40 
41 
42 
43 
44 
45 
46 
47 
48 
49 
50 

USE [CommunityServer2008]
GO
/****** Object:  StoredProcedure [dbo].[aspnet_Membership_GetUserByName]    Script Date: 09/22/2008 15:36:23 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[aspnet_Membership_GetUserByName]
    @ApplicationName      nvarchar(256),
    @UserName             nvarchar(256),
    @CurrentTimeUtc       datetime,
    @UpdateLastActivity   bit = 0
AS
BEGIN
    DECLARE @UserId uniqueidentifier

    IF (@UpdateLastActivity = 1)
    BEGIN
        SELECT TOP 1 m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
                m.CreateDate, m.LastLoginDate, @CurrentTimeUtc, m.LastPasswordChangedDate,
                u.UserId, m.IsLockedOut,m.LastLockoutDate
        FROM    dbo.aspnet_Applications a, dbo.aspnet_Users u, dbo.aspnet_Membership m
        WHERE    LOWER(@ApplicationName) = a.LoweredApplicationName AND
                u.ApplicationId = a.ApplicationId    AND
                LOWER(@UserName) = u.LoweredUserName AND u.UserId = m.UserId

        IF (@@ROWCOUNT = 0) -- Username not found
            RETURN -1

        UPDATE   dbo.aspnet_Users
        SET      LastActivityDate = @CurrentTimeUtc
        WHERE    @UserId = UserId
    END
    ELSE
    BEGIN
        SELECT TOP 1 m.Email, m.PasswordQuestion, m.Comment, m.IsApproved,
                m.CreateDate, m.LastLoginDate, u.LastActivityDate, m.LastPasswordChangedDate,
                u.UserId, m.IsLockedOut,m.LastLockoutDate
        FROM    dbo.aspnet_Applications a, dbo.aspnet_Users u, dbo.aspnet_Membership m
        WHERE    LOWER(@ApplicationName) = a.LoweredApplicationName AND
                u.ApplicationId = a.ApplicationId    AND
                LOWER(@UserName) = u.LoweredUserName AND u.UserId = m.UserId

        IF (@@ROWCOUNT = 0) -- Username not found
            RETURN -1
    END

    RETURN 0
END

testing

JavaScript:
1 
2 
3 
4 
5 
6 

var trTitle = document.all('WebPartCaptionWPQ10'); 
if(trTitle != null) 
{ 
var tdPrint = document.createElement("td"); 
var imgPrint = document.createElement("a"); 
}



Sorry, need to do another test :)

SQL:
1 
2 
3 
4 
5 
6 
7 
8 
9 
10 
11 
12 
13 
14 
15 
16 
17 
18 
19 
20 
21 
22 
23 
24 
25 
26 
27 

USE [CommunityServer20085]
GO
/****** Object:  StoredProcedure [dbo].[aspnet_Membership_FindUsersByEmail]    Script Date: 09/23/2008 15:23:56 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[aspnet_Membership_FindUsersByEmail]
    @ApplicationName       nvarchar(256),
    @EmailToMatch          nvarchar(256),
    @PageIndex             int,
    @PageSize              int
AS
BEGIN
    DECLARE @ApplicationId uniqueidentifier
    SELECT  @ApplicationId = NULL
    SELECT  @ApplicationId = ApplicationId FROM dbo.aspnet_Applications WHERE LOWER(@ApplicationName) = LoweredApplicationName
    IF (@ApplicationId IS NULL)
        RETURN 0

    -- Set the page bounds
    DECLARE @PageLowerBound int
    DECLARE @PageUpperBound int
    DECLARE @TotalRecords   int