Monday, January 17, 2005

 

Syntax coloring and folding in HTML

1.
使用js正则表达式格式化加亮源代码

来源:http://www.98xj1.com/users/mmkk/dhtml/FormatCsharpCodeByjs.html

一些站点上也有对某种语言的关键字进行加亮显示,比如CodeProject或者dotnetjunkies等,不过他们都是定义了特定的css样式,然后逐一进行样式应用来进行加亮显示,如果这种技术没有后台支持的话,将会有产生相当大的工作量,利用Javascript强大的正则表达式功能,我们可以在没有后台支持的情况下以少量的工作来完成这个功能,String.Replace()将会为我们完成所有核心工作
[code]
//替换关键字
for (thiskey in CsharpKeyword)
{
reg = new RegExp("\\b("+CsharpKeyword[thiskey].toString()+"){1}\\b","g");
text = text.replace(reg,"font color=blue>$1/font>");
}
//替换"..."字符串中可能被加亮的关键字
text = text.replace(/\"([^\"]*)\"/g,function($1){return $1.replace(/\/?font.*?>/gi,"")});
//.号表示除换行符或其他Unicode中止符之外的所有字符(替换单行注释符)
text = text.replace(/(\/\/.*)/g,function($1){return "font color=green>"+$1.replace(/\/?font.*?>/gi,"")+"/font>"});
text = text.replace(/(\/\*[\S\s]*\*\/)/g,function($1){return "font color=green>"+$1.replace(/\/?font.*?>/gi, "")+"/font>"});//替换多行注释符
[/code]

基本上,这里替换加亮特殊代码需要按照这样的步骤:替换关键字->替换字符串("")中的关键字(第一个步骤可能将某些字符串中的关键字加亮了,这个步骤需要把它们转回去)->替换//单行注释->替换/**/多行注释,每一个替换都要先擦掉原来的font>标记,才能解决嵌套的情况,否则有可能//afont color=red>b/font>c最终显示的是//abc而不是期望中的//afont color=red>b/font>c;这是最关键的地方.
需要注意的是,String.Replace(regex,replacement)方法;如果replacement是一个函数的话,必须要Javascript1.2或者Jscript5.5(IE5.5)以上才能实现

下面是HTC文件

[code]
PUBLIC:COMPONENT NAME="FormatCsharpCodeHTC">
PUBLIC:ATTACH EVENT="ondocumentready" ONEVENT="FormatCsharpCode()"/>
script language="Jscript">
//所有C#关键字
var CsharpKeyword = ["abstract","as","base","bool","break","byte","case","catch","char","checked",
"class","const","continue","decimal","default","delegate","do","double","else",
"enum","event","explicit","extern","false","finally","fixed","float","for","foreach",
"get","goto","if","implicit","in","int","interface","interal","is","lock","long",
"namespace","new","null","object","operator","out","override","params","private",
"protected","public","readonly","ref","return","sbyte","sealed","set","short",
"sizeof","stackalloc","static","string","struct","switch","this","throw","true",
"try","typeof","uint","ulong","unchecked","unsafe","ushort","using","value",
"virtual","void","while"];

function FormatCsharpCode()
{
var obj = eval(element.id);
var reg;
var text = obj.innerHTML;


for (thiskey in CsharpKeyword)
{
reg = new RegExp("\\b("+CsharpKeyword[thiskey].toString()+"){1}\\b","g");
text = text.replace(reg,"font color=blue>$1/font>");

}
//替换"..."字符串中可能被加亮的关键字
text = text.replace(/\"([^\"]*)\"/g,function($1){return $1.replace(/\/?font.*?>/gi,"")});
//.号表示除换行符或其他Unicode中止符之外的所有字符(替换单行注释符)
text = text.replace(/(\/\/.*)/g,function($1){return "font color=green>"+$1.replace(/\/?font.*?>/gi,"")+"/font>"});
text = text.replace(/(\/\*[\S\s]*\*\/)/g,function($1){return "font color=green>"+$1.replace(/\/?font.*?>/gi, "")+"/font>"});//替换多行注释符
obj.innerHTML = text;
obj.style.backgroundColor = "#F7F3F7";
obj.style.border = "1px solid #696969";
obj.style.padding = "2px;"
obj.style.margin = "2px";
}

/PUBLIC:COMPONENT>
[/code]

2.
原理相近,但更加复杂的例子可以参见Jonathan de Halleux的文章,这家伙看起来闲散时间极多。

Multiple Language Syntax Highlighting, Part 1: JScript
http://www.codeproject.com/jscript/highlight.asp?target=syntax%7Ccoloring
Multiple Language Syntax Highlighting, Part 2: C# Control
http://www.codeproject.com/csharp/highlightcs.asp )

3.
http://www.actiprosoftware.com/Products/DotNet/CodeHighlighter/PasteCode.aspx
这个更强,use DHTML to enable outlining (IE only) 类似VS.NET中的折叠效果。但是需要特定的图片,而且生成的html代码多了很多的img标签。
最后,需要把生成的html代码放入一个pre标签中,一段美观的code snippet就生成了。
5/8/2004补充

4. 搜查结果关键词的加亮
我们知道Lucene和Lucene.Net是预先生成好了相应的加亮code,以HTML的方式传给用户。这其实是一个比较简单的字符串替换问题,可以参见Lucene.Net的相关代码
https://sourceforge.net/projects/lucenedotnet/

当然,我们也可以使用后期着色的方法。比如Google的方法就是参照下面的文档写的
http://www.kryogenix.org/code/browser/searchhi/

5. 以前的blog讨论了highliting,现在folding正在成为越来越多网站上的基本功能。其实现手法主要有两种:

第一种是使用javascript,这个速度慢,实现代码也比较困难。
另一种广为使用的方法是使用高版本的IE所具有的Table处理功能来实现。这种方式将代码分层插入一个嵌一个的Table Cell中,再加上合适的几个图片,就可以相对容易的实现folding,就像IE显示XML文件一般。这种方法还是比较巧妙的,其关键还是原始code文档的XML化和分隔符号的寻找。这方面的代码可以从多个地方看到,actiprosoftware公司的产品旧很不错。

http://www.actiprosoftware.com/Products/DotNet/Default.aspx

不过,由于XML化的速度不高,代码量大的话,速度肯定低,内存也耗的多(因为要把数据全部读入内存)。下面是一个例子

By Teddy
From http://www.cnblogs.com/teddyma/archive/2005/01/10/89466.html

写了一个全文本的C#代码着色器,不采用任何图片,因为事先没有参考任何已有着色器源码,可能有些地方实现的不是很优雅,如果谁手里有其他着色器的源码,望多交流!

using System;

using System.Text.RegularExpressions;



namespace CN.Teddy.Util.HtmlCodeColor

{

/// summary>

/// C#代码着色适配器

/// /summary>

public class CSharpAdapter : ICodeColorAdapter

{

_ #region Constructors



public CSharpAdapter()

{

//null

}



- #endregion



_ #region Private Members



/// summary>

/// 为//和/**/类型的注释着色

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

private string ColorBasicComment(string src)

{

string retCode = src;



Regex r1 = new Regex(@"(^|;)([ \t]*)(//.*$)", RegexOptions.Multiline);

retCode = r1.Replace(retCode, "$1$2 span style=\"color: green\">$3/span>");

Regex r2 = new Regex(@"(^|[ \t]+)(/\*[^\*/]*\*/[ \t\r]*$)", RegexOptions.Multiline);

retCode = r2.Replace(retCode, new MatchEvaluator(this.ColorBasicComment2Evaluator));



return retCode;

}



/// summary>

/// 为///类型的注释着色

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

private string ColorXmlComment(string src)

{

string retCode = src;



Regex r1 = new Regex(@"(/// *)<(.*)>", RegexOptions.Multiline);

retCode = r1.Replace(retCode, new MatchEvaluator(this.ColorXmlCommentEvaluator));

Regex r2 = new Regex(@"(/// *)(.*$)", RegexOptions.Multiline);

retCode = r2.Replace(retCode, "span style=\"color: gray\">$1/span>$2");



return retCode;

}



/// summary>

/// 为折叠显示代码构建HtmlTable框架

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

private string DrawCollapseFrameTable(string src)

{

System.Text.StringBuilder retCode = new System.Text.StringBuilder();



string frameHeader = "table border=0 cellpadding=0 cellspacing=0 width=95% style='border: windowtext 0.5pt solid; background-color:#fefefe'>tr>td valign=top align=center style='border-right-color: #808080; border-right-style: solid; border-right-width: 1px'>table border=0 cellpadding=0 cellspacing=0>tr>td> /td>td> /td>/tr>/table>/td>td>table border=0 cellpadding=1 cellspacing=0 width=100%>";

string frameTailer = "/table>/td>/tr>/table>";



retCode.Append(frameHeader);



string[] lines = src.Split('\n');



foreach (string line in lines)

{

string formatedLine = line.Trim();



string lineHeader = "tr>td style='width: 0px'>table style='width: 9px; height: 9px'>tr>td>/td>/tr>/table>/td>td style='width: 0px'>/td>td style='width: 100%'>code style='font: 9pt Tahoma'>";

string lineTailer = "/code>/td>/tr>";



formatedLine = lineHeader + formatedLine + lineTailer;



retCode.Append(formatedLine);

}



retCode.Append(frameTailer);



return retCode.ToString();

}



/// summary>

/// 折叠Region

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

private string CollapseRegion(string src)

{

string retCode = src;



string lineHeader = "tr>td style='width: 0px'>table style='width: 9px; height: 9px'>tr>td>/td>/tr>/table>/td>td style='width: 0px'>/td>td style='width: 100%'>code style='font: 9pt Tahoma'>";



Regex r = new Regex(lineHeader + @"( )*span style=\""color: blue\"">#region.*$", RegexOptions.Multiline);

while (r.Match(retCode).Success)

{

//add "+" tag

retCode = r.Replace(retCode, new MatchEvaluator(this.CollapseRegionEvaluator));

}



return retCode;

}

/// summary>

/// 为一行源码中的关键字着色

/// /summary>

/// param name="codeLine">某一行源码/param>

/// param name="keywordList">关键字列表/param>

/// returns>格式化后的源码/returns>

/// summary>

private string ColorKeyword(string codeLine, string[] keywordList)

{

string retCode = codeLine;



if (!retCode.StartsWith("//"))

{

foreach (string keyword in KEYWORD_LIST)

{

Regex r = new Regex(@"(^|\s+|,|\)|\(|\{|\}|\[|\]|\.|=|;)(" + keyword + @")(\s+|,|\)|\(|\{|\}|\[|\]|\.|=|;|$)");



retCode = r.Replace(retCode, "$1span style=\"color: blue\">$2/span>$3");

}

}



retCode = ClearColoredKeyworsInString(retCode);



return retCode.Trim();

}



/// summary>

/// 清除字符串中的被着色的关键字

/// /summary>

/// param name="codeLine">某一行源码/param>

/// returns>格式化后的源码/returns>

private string ClearColoredKeyworsInString(string codeLine)

{

System.Text.StringBuilder retCode = new System.Text.StringBuilder();



string str = codeLine.Trim();



int indexOfQuot = str.IndexOf(""");

int lengthOfQuot = """.Length;



if (indexOfQuot >= 0)

{

while (indexOfQuot >= 0)

{

retCode.Append(str.Substring(0, indexOfQuot + lengthOfQuot));

str = str.Substring(indexOfQuot + lengthOfQuot);



indexOfQuot = str.IndexOf(""");



if (indexOfQuot >= 0)

{

string inStr = str.Substring(0, indexOfQuot + lengthOfQuot);



inStr = inStr.Replace("\"color: blue\"", "\"\"");



retCode.Append(inStr);

str = str.Substring(indexOfQuot + lengthOfQuot);

}



indexOfQuot = str.IndexOf(""");

}

}



retCode.Append(str);



return retCode.ToString();

}





public static string[] KEYWORD_LIST = {

"abstract", "event", "new", "struct", "as", "explicit", "null", "switch",

"base", "extern", "object", "this", "bool", "false", "operator", "throw",

"break", "finally", "out", "true", "byte", "fixed", "override", "try",

"case", "float", "params", "typeof", "catch", "for", "private", "uint",

"char", "foreach", "protected", "ulong", "checked", "goto", "public",

"unchecked", "class", "if", "readonly", "unsafe", "const", "implicit",

"ref", "ushort", "continue", "in", "return", "using", "decimal", "int",

"sbyte", "virtual", "default", "interface", "sealed", "volatile",

"delegate", "internal", "short", "void", "do", "is", "sizeof", "while",

"double", "lock", "stackalloc", "else", "long", "static", "enum",

"namespace", "string", "get", "set", "#region", "#endregion", "true",

"false"

};



private string ColorXmlCommentEvaluator(Match m)

{

Regex r = new Regex("(^.*>)(.*)(</.*$)");

if (r.Match(m.Value).Success)

{

return r.Replace(m.Value, "span style=\"color: gray\">$1/span>$2span style=\"color: gray\">$3/span>");

}

else

{

return "span style=\"color: gray\">" + m.Value + "/span>";

}

}



private string ColorBasicComment2Evaluator(Match m)

{

System.Text.StringBuilder retCode = new System.Text.StringBuilder();



string[] lines = m.Value.Split('\n');



foreach (string line in lines)

{

retCode.Append("span style=\"color: green\">" + line + "/span>" + "\n");

}



return retCode.ToString();

}



private string CollapseRegionEvaluator(Match m)

{

//get region code block



string retCode = m.Value;



string lineHeader = "tr>td style='width: 0px'>table style='width: 9px; height: 9px'>tr>td>/td>/tr>/table>/td>td style='width: 0px'>/td>td style='width: 100%'>code style='font: 9pt Tahoma'>";

string lineTailer = "/code>/td>/tr>";



Regex r = new Regex("^" + lineHeader + @"( )*span style=\""color: blue\"">#region/span>[^]*" + lineTailer, RegexOptions.Multiline);

retCode = r.Replace(retCode, new MatchEvaluator(this.CollapseRegionEvaluator2));



//find out #region - #endregion block & add collapse code

string endRegionLinePattern = "tr>td style='width: 0px'>table style='width: 9px; height: 9px'>tr>td>/td>/tr>/table>/td>td style='width: 0px'>/td>td style='width: 100%'>code style='font: 9pt Tahoma'>( )*span style=\"color: blue\">#endregion/span>/code>/td>/tr>";

Regex r2 = new Regex(endRegionLinePattern);

string endRegionLine = r2.Match(retCode).Value;

string formatedEndRegionLine = endRegionLine.Replace("td style='width: 0px'>/td>td style='width: 100%'>", "td style='width: 0px'>span style='position: relative; left: -12px; font: 12px'>font color=#808080 face='Tahoma'>-/font>/span>/td>td style='width: 100%'>");

string regionBlock = retCode.Substring(0, retCode.IndexOf(endRegionLine)) + formatedEndRegionLine;



string spaces = new Regex("( )+").Match(regionBlock).Value;

string regionDescWidthTag = new Regex("#region/span> ([^]*)/code>").Match(regionBlock).Value;

string regionDesc = regionDescWidthTag.Substring(15, regionDescWidthTag.Length - ("#region/span> ").Length - ("/code>").Length);



regionBlock = regionBlock.Replace("/tr>tr>td style='width: 0px'>", "/tr>tr style='display: none'>td style='width: 0px'>");

regionBlock = regionBlock.Substring(0, regionBlock.IndexOf("code style='font: 9pt Tahoma'>") + "code style='font: 9pt Tahoma'>".Length) +

"span>" + spaces + "/span>" + "span style='border: 1px solid gray'>font color=gray>" + regionDesc + "/font>/span>" +

regionBlock.Substring(regionBlock.IndexOf("/code>"));



retCode = regionBlock + retCode.Substring(retCode.IndexOf(endRegionLine) + endRegionLine.Length);



return retCode;

}





private string CollapseRegionEvaluator2(Match m)

{

string retCode = m.Value;



//add rectangle

retCode = retCode.Replace("style='width: 9px; height: 9px'", "style='position: relative; left: -6px; top: 0px; border-color: #808080; border-style: solid; border-width: 1px; background: white; width: 9px; height: 9px'");



string spaces = new Regex("( )+").Match(m.Value).Value;

string regionDescWidthTag = new Regex("#region/span> ([^]*)/code>").Match(m.Value).Value;

string regionDesc = regionDescWidthTag.Substring(15, regionDescWidthTag.Length - ("#region/span> ").Length - ("/code>").Length);



//add "+" tag

retCode = retCode.Replace("td style='width: 0px'>/td>", "td style='width: 0px'>span style='position: relative; left: -16px; top: -1px; font: 12px; cursor: hand' onclick='if (this.childNodes[0].innerHTML == \"_\") { this.childNodes[0].innerHTML = \"+\"; this.style.top = \"-1px\"; this.parentNode.nextSibling.childNodes[0].innerHTML = \"span>" + spaces + "/span>span>font color=gray>" + regionDesc + "/font>/span>\"; this.parentNode.nextSibling.childNodes[0].childNodes[1].style.border = \"1px solid gray\"; for (var i = 1 + this.parentNode.parentNode.rowIndex; i this.parentNode.parentNode.parentNode.childNodes.length; i++) { this.parentNode.parentNode.parentNode.childNodes[i].style.display=\"none\"; if ( this.parentNode.parentNode.parentNode.childNodes[i].childNodes[1].innerText.length > 0 ) { break; } } } else { this.childNodes[0].innerHTML = \"_\"; this.style.top = \"-6px\"; var oldDesc = this.parentNode.nextSibling.childNodes[0].childNodes[1].childNodes[0].innerHTML; this.parentNode.nextSibling.childNodes[0].innerHTML = \"span>" + spaces + "/span>span>font color=blue>#region /font>/span>span>/span>\"; this.parentNode.nextSibling.childNodes[0].childNodes[2].innerHTML = oldDesc; for (var i = 1 + this.parentNode.parentNode.rowIndex; i this.parentNode.parentNode.parentNode.childNodes.length; i++) { this.parentNode.parentNode.parentNode.childNodes[i].style.display=\"block\"; if ( this.parentNode.parentNode.parentNode.childNodes[i].childNodes[1].innerText.length > 0 ) { break; } } }'>font color=#808080 face='Tahoma'>+/font>/span>/td>");



return retCode;

}



- #endregion



_ #region ICodeColorAdapter 成员



/// summary>

/// 为注释着色,例如:对C#,包括///型、//型和/**/的注释

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

public string ColorComment(string src)

{

string retCode = src;

retCode = ColorBasicComment(retCode);

retCode = ColorXmlComment(retCode);

return retCode;

}



/// summary>

/// 为关键字着色

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

public string ColorKeyword(string src)

{

System.Text.StringBuilder retCode = new System.Text.StringBuilder();



string[] lines = src.Split('\n');



if (lines != null && lines.Length > 0)

{

bool isInComment = false;



foreach (string line in lines)

{

string formatedLine = line.Trim();



if (new Regex(@"(^|[ \t]+)(/\*)").Match(line).Success)

{

isInComment = true;

}



if (!isInComment)

{

formatedLine = ColorKeyword(line, KEYWORD_LIST);

}



if (new Regex(@"\*/[ \t\r]*$").Match(line).Success)

{

isInComment = false;

}



retCode.Append(formatedLine + "\n");

}

}



return retCode.ToString();

}



/// summary>

/// 使代码支持折叠显示

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

public string CollapseCode(string src)

{

string retCode = src;

retCode = DrawCollapseFrameTable(retCode);

retCode = CollapseRegion(retCode);

return retCode;

}



/// summary>

/// 代码缩进

/// /summary>

/// param name="src">输入源码/param>

/// returns>格式化后的源码/returns>

public string IndentCode(string src)

{

System.Text.StringBuilder retCode = new System.Text.StringBuilder();



int indent = 0;

string [] lines = src.Split('\n');



foreach (string line in lines)

{

string formatedLine = line.Trim();



Regex r = new Regex(@"\}(\}|\s)*;?\s*$");

if (r.Match(line).Success)

{

indent--;

}



for (int i = 0; i indent; i++)

{

formatedLine = "      " + formatedLine;

}



retCode.Append(formatedLine + "\n");



if (line.EndsWith("{"))

{

indent++;

}

else if (line.StartsWith("{"))

{

indent++;

}

}



return retCode.ToString();

}

- #endregion

}

}

这里显然缺少关键字着色的部分。加上最前面说得代码就完美啦。

Another one
Library Code2HTML
From DotNetJunkie XL on the 7th Floor
http://dotnetjunkies.com/WebLog/kris/archive/2003/11/04/3171.aspx

BTW, if you have only Vision 2003 or below at hand and you need to draw UML. You have

Visio Stencil and Template for UML 2.0
http://www.phruby.com/stencildownload.html



<< Home

This page is powered by Blogger. Isn't yours?