一个简单的语法高亮实现

xingqiliudehuanghun 2011-03-31 01:56:52
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>color keywords</title>
<style type="text/css">
* {
font-family: "新宋体";
}

#txtCode{
width : 100%;
height: 200px;
resize: vertical;
}

.Output{
border : 1px solid #396;
color : white;
width : 100%;
resize : both;
font-size : 16px;
line-height : 150%;
background-color:#0B161D;
}

Span.Comments{
color:#159AE0
}

Span.String{
color:#27A735;
}

Span.RegExp{
color:#EA842B;
}

Span.Keywords{
color:#FFAA00;
}

Span.Number{
color:#9C3583;
}

Span.Operator{
color:#8CBBAD;
}
</style>

<script type="text/javascript">
function colorKeywords(){
var keywords = "Array|arguments|alert|window|document|location|Boolean|Date|Enumerator|Error|Function|Global|Math|Number|Object|RegExp|String|break|delete|function|return|typeof|case|do|if|switch|var|catch|else|in|this|void|continue|false|instanceof|throw|while|debugger|finally|new|true|with|default|for|null|try|abstract|double|goto|native|static|boolean|enum|implements|package|super|byte|export|import|private|synchronized|char|extends|int|protected|print|throws|class|final|interface|public|transient|const|float|long|short|volatile";

//解析优先级: 注释 > 字符串 > 正则 > 关键字 > 数字 > 运算符
var regStr = "(/\\*[\\S\\s]*?\\*/|//.*?\\r?\\n)"
+ "|((?:\"(?:[^\"]*?\\\\\")*.*?\")|(?:'(?:[^']*?\\\\')*.*?'))"
+ "|(/(?:[^/]+?\\\\/)*.*?/[a-zA-Z]*)"
+ "|(\\b(?:"+keywords+")\\b)"
+ "|(\\b\\d+\\b)"
+ "|(\\+\\+|--|===|==|\\+=|-=|\\*=|%=|~=|\\^=|\\|=|\\+|\\-|\\*|%|=|\\?|\\:|\\{|\\}|\\(|\\)|\\[|\\]|&&|&|\\|\\||\\|)";

var reg = new RegExp(regStr, "gi");
var source = $("txtCode").value;
if( source == "" ) return;

var ds = new Date();
//着色前对特殊字符:<、>进行处理,否则格式会乱掉。
source = source.replace(/</g, "<")
.replace(/>/g, ">");

//对关键字进行着色
var output = source.replace(reg, function(){
var args = [].slice.call(arguments, 0);
var lstIdx = args[args.length - 2];
var match = args[0];

if( args[1] ) return "<span class='Comments'>" + args[1] + "</span>";
if( args[2] ) return "<span class='String'>" + args[2] + "</span>";
if( args[3] ) return "<span class='RegExp'>" + args[3] + "</span>";

if( args[4] ) return (source.charAt(lstIdx-1) != "$")
? "<span class='Keywords'>"+ args[4] + "</span>"
: match;

if( args[5] ) return (source.charAt(lstIdx-1) != "$")
? "<span class='Number'>" + args[5] + "</span>"
: match;

if( args[6] ) return "<span class='Operator'>" + args[6] + "</span>";

return match;
});

//替换掉空格和回车使能够正常格式化输出
output = output.replace(/\r?\n/g, "<br/>" )
.replace(/\t/g , "    ")
.replace(/(<\/?\w+.*?>)|(\s)/g, function($0, $1, $2){
if( $1 ) return $1;
if( $2 ) return " ";
});

var de = new Date();

alert("耗时:" + (de - ds));

$("output").innerHTML = output;
}


function $(id){
return document.getElementById(id);
}


function clearOutput(){
$("output").innerHTML="";
}
</script>
</head>
<body>

<input type="button" value="代码着色" onClick="colorKeywords()" />
<input type="button" value="情况输出" onClick="clearOutput()" />
<br/>
<br/>

请输入要着色的代码:
<textarea id="txtCode">
/**---------------------------
* test multiline comments
* @author jianbo.wang
*/

function(){
//测试单行注释着色

/**--------------------------
* 测试多行注释着色.
*--------------------------*/

//测试字符串着色.
print("\"hello world!\"");
print('will output:"hello world!"');

//测试正则着色
var reg = /(<\/?\w+.*?>)|(\s)/g;

//测试关键字着色
var output, _window, $window,
c= window.alert, d= $window.alert, e= _window.alert,
f= window._alert, g= $window._alert, h= _widnow._alert
i= window.$alert, j= $window.$alert, k= _window.$alert;

for(var i=0; i<100; i++){
output = "Welcome user" + i;
alert(output);
}

//测试数字着色
var a = 100, b= "200", $100, _200, c="$200", d="_100";


//运算符着色测试
var index = 0;
var user = {
name : "张三",
age : 12,
index: index++
};

var a = 0;
a++; a--; a+="hello"; a+=100; a-=1; a*=1; a%=1;
(a === 1 ) ? true : false;
(a == 1 ) ? true : false;
(a = 1 ) ? true : false;
(a > 1 ) ? true : false;
}

function colorKeywords(){
//解析优先级: 注释 > 字符串 > 正则 > 关键字 > 数字 > 运算符
var keywords = "Array|arguments|alert|window|document|location|Boolean|Date|Enumerator|Error|Function|Global|Math|Number|Object|RegExp|String|break|delete|function|return|typeof|case|do|if|switch|var|catch|else|in|this|void|continue|false|instanceof|throw|while|debugger|finally|new|true|with|default|for|null|try|abstract|double|goto|native|static|boolean|enum|implements|package|super|byte|export|import|private|synchronized|char|extends|int|protected|print|throws|class|final|interface|public|transient|const|float|long|short|volatile";
var regStr = "(/\\*\\*[\\S\\s]*?\\*/|//.*?\\r?\\n)"
+ "|((?:\"(?:[^\"]+?\\\\\")*.*?\")|(?:'(?:[^']+?\\\\')*.*?'))"
+ "|(/(?:[^/]+?\\\\/)*.*?/[a-zA-Z]*)"
+ "|(\\b(?:"+keywords+")\\b)"
+ "|(\\b\\d+\\b)"
+ "|(\\+\\+|--|===|==|\\+=|-=|\\*=|%=|~=|\\^=|\\|=|\\+|\\-|\\*|%|=|\\?|\\:|\\{|\\}|\\(|\\)|\\[|\\])";

var reg = new RegExp(regStr, "gi");
var source = $("txtCode").value;
if( source == "" ) return;

//着色前对特殊字符:<、>进行处理,否则格式会乱掉。
source = source.replace(/</g, "<")
.replace(/>/g, ">");

//对关键字进行着色
var output = source.replace(reg, function(){
var args = [].slice.call(arguments, 0);
if( args[1] ) return "<span class='Comments'>" + args[1] + "</span>";
if( args[2] ) return "<span class='String'>" + args[2] + "</span>";
if( args[3] ) return "<span class='RegExp'>" + args[3] + "</span>";
if( args[4] ) return "<span class='Keywords'>" + args[4] + "</span>";
if( args[5] ) return "<span class='Number'>" + args[5] + "</span>";
if( args[6] ) return "<span class='Operator'>" + args[6] + "</span>";
return args[0];
});

//替换掉空格和回车使能够正常格式化输出
output = output.replace(/\r?\n/g, "<br/>" )
.replace(/\t/g , "    ")
.replace(/(<\/?\w+.*?>)|(\s)/g, function($0, $1, $2){
if( $1 ) return $1;
if( $2 ) return " ";
});

$("output").innerHTML = output;
}


function $(id){
return document.getElementById(id);
}


function clearOutput(){
$("output").innerHTML="";
}
</textarea>
<br/>
<br/>
代码着色结果:
<div id="output" class="Output">
codes output here!
</div>

</body>
</html>
...全文
715 60 打赏 收藏 转发到动态 举报
写回复
用AI写文章
60 条回复
切换为时间正序
请发表友善的回复…
发表回复
inthello 2012-05-02
  • 打赏
  • 举报
回复
什么东西楼主
  • 打赏
  • 举报
回复
抽空做了写小改动,优化了下数字和字符串的处理,使其更精确, 正则部分还有些问题,有空再改吧

<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>color keywords </title>
<style type="text/css">
* {
font-family: "新宋体";
}

#txtCode{
width : 100%;
height: 200px;
resize: vertical;
}

b.Comments{
color:#159AE0;
font-weight:500;
}

b.String{
color:#27A735;
font-weight:500;
}

b.RegExp{
color:#EA842B;
font-weight:500;
}

b.Keywords{
color:#FFAA00;
font-weight:500;
}

b.Number{
color:#9C3583;
font-weight:500;
}

b.Operator{
color:#FFAA00;
font-weight:500;
}

b.LineNo{
background-color : #2e3436;
margin-right : 5px;
color : #8CBBAD;
font-weight : 500;
}

.Output{
border : 1px solid #396;
color : white;
resize : both;
font-size : 16px;
width : 100%;
word-wrap : no-wrap; /*使不自动换行, 调试中*/
word-break : keep-all;
background-color: #0B161D;
}
</style>

<script type="text/javascript">
function sytaxHightlight(source, outEl, keywords, optrs, cfg){
if((!source || !outEl)
|| !(keywords instanceof Array)
|| !(optrs instanceof Array)
|| keywords.length ===0
|| optrs.length === 0){
return;
}

var ds = new Date();

//对关键字和运算符进行排序
var sortFn = function(i1, i2){return i1 < i2 ? 1 : -1;};
keywords = keywords.concat().sort(sortFn);
optrs = optrs.concat().sort(sortFn);

//生成解析表达式
var comntsPattn = '/\\*[\\S\\s]*?\\*/|//.*?\\r?\\n',
strPattn = '\'(?:[^\\\\\'\\n]|\\\\.)*\'|"(?:[^\\\\"\\n]|\\\\.)*"',
regPattn = '/(?:[^/]+?\\\\/)*.*?/[a-zA-Z]*',
namePattn = '[_\\$a-zA-Z][_\\$\\w]*',

numberPattn = (function(){
var haxPart = "0x[0-9a-fA-F]+",
numPart = "(?:(?:0?\\.\\d+)|(?:[1-9]\\d*(?:\\.\\d+)?))",
expPart = "(?:(?:e|E)(?:(?:\\+|-)?(?:0|[1-9]\\d*)))?";

return '(?:' + haxPart + ')|(?:' + numPart + expPart + ')';
})(),

optrPattn = (function(){
var i, optr, ret = [],
len = optrs.length,
reg = /(\+|\*|\?|\!|\:|\^|\$|\||\{|\}|\(|\)|\[|\])/g;

for(i = 0; i < len; i++){
optr = optrs[i];
if(optr){
ret.push(optr.replace(reg, "\\$1"));
}
}

return ret.join('|');
})(),

//解析优先级: 注释 > 字符串 > 正则 > 标示符(变量) > 数字 > 运算符 > \s\n\t等占位符
parseReg = new RegExp((
'(' + comntsPattn + ')'
+ '|(' + strPattn + ')'
+ '|(' + regPattn + ')'
+ '|(' + namePattn + ')'
+ '|(' + numberPattn + ')'
+ '|(' + optrPattn + ')' //不需要的话可以去掉此行并相应调整source.replace
+ '|(\\r?\\n|\\s|\\t)' //注意\s会匹配换行符\n, 因此换行符的匹配需要提前
), 'g'),

//检验标示符是否为关键字
isKeyWord = (function(){
var i, len = keywords.length; kwMap = {};
for(i = 0; i < len; i++){
kwMap[keywords[i]] = null;
}

return function(name){
return kwMap.hasOwnProperty(name);
};
})();

//格式化数字函数
var formatNo = function(n){
if( n < 10 ){
return "    " + n;
}else if(n < 100 ){
return "   " + n;
}else if(n < 1000){
return "  " + n;
}else if(n < 10000){
return " " + n;
}else{
return n;
}
};

//处理字符串中的特殊字符,并在需要换行的时候添加行号
var lineNo = 1;
var handleStr = function(str){
return str.replace(/<|>|&|\r?\n|\s|\t/g, function(match){
switch( match ){
case "<" : return "<" ;
case ">" : return ">" ;
case "&" : return "&" ;
case " " : return " " ;
case "\t" : return "    ";

case "\n" :
case "\r\n" :
return "<br/><b class='LineNo'>" + formatNo(++lineNo) + ".</b>";
}
});
};

//对关键字进行着色
var output = source.replace(parseReg, function(){
var args = arguments, match = args[0];

if (args[1]) return "<b class='Comments'>" + handleStr(match) + "</b>";
if (args[2]) return "<b class='String' >" + handleStr(match) + "</b>";
if (args[3]) return "<b class='RegExp' >" + handleStr(match) + "</b>";
if (args[4]) return isKeyWord(match)
? "<b class='Keywords'>"+ match + "</b>"
: match;
if (args[5]) return "<b class='Number'>" + match + "</b>";
if (args[6]) return "<b class='Operator'>" + handleStr(match) + "</b>";
if (args[7]) return handleStr(match);

return match;
});
output = "<b class='LineNo'>" + formatNo(1) + ".</b>" + output;

//计算耗时
var de = new Date();
alert("处理字符串耗时:" + (de - ds));

//输出格式化后的内容
outEl.innerHTML = output;
}

//辅助代码---------------------------------------
function $(id){
return document.getElementById(id);
}

function clearOutput(){
$("output").innerHTML="";
}

function getStyle(lang){
var map = {};

map['js'] = map['javascript'] = {
keywords : (
"Array|arguments|alert|window|document|location|Boolean|Date|Enumerator|Error|Function|Global|Math|Number|Object|RegExp|String|break|delete|function|return|typeof|case|do|if|switch|var|catch|else|in|this|void|continue|false|instanceof|throw|while|debugger|finally|new|true|with|default|for|null|try|abstract|double|goto|native|static|boolean|enum|implements|package|super|byte|export|import|private|synchronized|char|extends|int|protected|print|throws|class|final|interface|public|transient|const|float|long|short|volatile"
).split(/\|/),

optrs : (
"+ ++ - -- * / % "
+ "+= -= *= /= %= ^= &= "
+ "^ ! ~ >>> <<< "
+ "> >= < <= != !== === == = "
+ "& && | || "
+ ", ? : ; "
+ "{ } [ ] ( )"
).split(/\s+/)
};

return map[lang.toLowerCase()];
}

function doHightlight(){
var source = $("txtCode").value,
outEl = $('output' ),
style = getStyle('js'),
keywords= style.keywords,
optrs = style.optrs;

sytaxHightlight(source, outEl, keywords, optrs);
}
</script>
</head>
<body>

<input type="button" value="代码着色" onClick="doHightlight()" />
<input type="button" value="清空输出" onClick="clearOutput()" />
<br/>
<br/>

请输入要着色的代码:
<textarea id="txtCode">
/**---------------------------
* test multiline comments
* @author jianbo.wang
*/

function(){
//测试单行注释着色

/**--------------------------
* 测试多行注释着色.
*--------------------------*/

//测试字符串着色.
print("\"hello world!\"");
print('will output:"hello world!"');
print('hello \\\'world!\\\"')

//一些错误的字符串
print('hello \\'world!\\\"')
print("hello \\"world!\\\"")
print('hello world!")
print("hello world!')
print("hello world!)
print('hello world!)
print(\'hello world!)
print(\"hello world!)

//测试正则着色
var reg = /(<\/?\w+.*?>)|(\s)/g;

//测试关键字着色
var output, _window, $window,
c= window.alert, d= $window.alert, e= _window.alert,
f= window._alert, g= $window._alert, h= _widnow._alert
i= window.$alert, j= $window.$alert, k= _window.$alert;

for(var i=0; i <100; i++){
output = "Welcome user" + i;
alert(output);
}

if (a instanceof Array) return true;
alert(typeof a);
var a = new String('abc');

//测试数字着色
var $1, $2, _1, _2, a100 = 100, b12= "200", $100, _200, c="$200", d="_100";
var nums = [
0x123, 0xfff, 0x0, 0x12F,
.123, 0.56, 100, -11.2, +12.3,
0.5e16, 0.5E-3, .6E12, .6E-2
];



//运算符着色测试
var index = 0;
var user = {
name : "张三",
age : 12,
index: index++
};

var a = 0;
a++; a--; a+="hello"; a+=100; a-=1; a*=1; a%=1; a>=1; a <=1; a > b; a != 2;
(a === 1 ) ? true : false;
(a == 1 ) ? true : false;
(a = 1 ) ? true : false;
(a > 1 ) ? true : false;
if( a > 1 || a < 0 || (a == "string" && b == "number" ) return false;
}

</textarea>
<br/>
<br/>

代码着色结果:
<div id="output" class="Output" >
codes output here!
</div>

</body>
</html>
vchalf_moon 2011-07-06
  • 打赏
  • 举报
回复
新手学习
果-果 2011-07-06
  • 打赏
  • 举报
回复
谢谢LZ,顶一下,收藏 了
  • 打赏
  • 举报
回复
[Quote=引用 52 楼 dongtianlaile 的回复:]
呵呵,顶一下 PS:楼主在淘宝呆过吗?
[/Quote]
没有啊?怎么这么问啊
mamxfx 2011-07-06
  • 打赏
  • 举报
回复
顶一脚。。期待下次贴知识贴。。。。
dongtianlaile 2011-07-06
  • 打赏
  • 举报
回复
呵呵,顶一下 PS:楼主在淘宝呆过吗?
班门弄斧 2011-07-06
  • 打赏
  • 举报
回复
留下脚印
bjqszcy1991 2011-07-06
  • 打赏
  • 举报
回复
新手学习
confidenceyu 2011-07-06
  • 打赏
  • 举报
回复
效果蛮好,但不知道什么时候会用到这种高亮 ,新手问
  • 打赏
  • 举报
回复
数字的处理还不够,有些格式比如:.123 1e-10等都还不支持
varlj 2011-06-29
  • 打赏
  • 举报
回复
效果挺好的,试了几种比较容易出现着色错误的语法,也没问题
  • 打赏
  • 举报
回复
因为比较简单,所以会快一些。等比较闲了想把代码格式化的功能也加上去
ziyouren521125 2011-06-29
  • 打赏
  • 举报
回复
顶分享............
ci1699 2011-06-29
  • 打赏
  • 举报
回复
chrom 效率很高啊。待会分析代码学习学习。支持lz
dautlu 2011-06-29
  • 打赏
  • 举报
回复
下载看看..
kkjjww 2011-04-18
  • 打赏
  • 举报
回复
这个不错~~~
  • 打赏
  • 举报
回复
这两天忙着产品上线,没时间打理帖子,没想到还有关注。谢谢大家的支持,这两天正在看Douglas Crockford
的一些代码为格式化脚本做一些技术准备。
neubaixu 2011-04-17
  • 打赏
  • 举报
回复
楼主辛苦 一定要顶
ChargeForward 2011-04-17
  • 打赏
  • 举报
回复
mark 过两天正好用得着
加载更多回复(31)
用于windows下面的vim编辑器。 给喜欢vim的人使用。详细如下: 好吧,详细点,这里写出一些基本的命令: 【什么是vi】 vi就是linux命令行下的最著名的编辑器之一,(编辑器就是类似windows记事本的功能,不过vi功能比记事本强大无限倍!^_^),现在实际使用的都是vim,它是vi的改进版本,所以现在的vi基本上就是vim了。 【vi能做什么】 第一个功能不用多说,vi可以编辑文本文件。至于其他的功能,多得无法列举,我只说我所知的一些典型的功能应用: (1)编辑文本文件 (2)方便地阅读程序源代码 (3)当文件目录浏览器来用 (4)提供编程、调试环境 (5)利用vi执行一些脚本文件(vi有自己的脚本文件语法) (x)等等等等...... 【vi的操作模式】   vi具有两种基本模式,为输入模式(或插入模式、编辑模式)和指令模式(或命令模式)。输入模式下输入字符,文本就会显示在屏幕上;而指令模式下输入字符就解释为一个输入命令并执行,而不会显示相应的字符。理解指令模式最简单方式就是想象平时剪切、复制以及粘贴数据时所发生的情况。 使用[Esc]键可以停止当前操作(插入或者命令)重新回到指令模式,准备接受新的指令。如果本来就在指令模式下面,将会响铃一下。 【最基本的使用】 这里提供了使用vi得最基本的操作,能够实现大多数的编辑目的。如果你肯花几分钟把“最基本的使用”的内容都实践一下,那么你至少可以独立地在vim中朝你想要得方向“前进”了。如果你不想学习,那么就把它当作一个“字典”吧。 *用vi打开一个文件: 输入“vi filename”. 这里,filename就是你要打开的文件的名字,默认打开文件后vi处于指令模式。 *进入编辑模式编辑打开的文件: 输入“i”. 或输入“a”. 进入编辑模式后,你可以直接敲入想要输入的字符到文件,两者的区别是i在当前字符前面开始插入,a在当前字符后面开始插入。 *退出编辑模式: 输入“[Esc]”. 这样,将返回指令模式,准备接收你要传达给vi的指令并执行,如果之前已经在指令模式下,那么系统将响铃提醒一下。 **以下命令都是在命令模式下进行: *撤销修改: 输入“u”. 这里,相比以前的vi来说,vim支持多步撤销。 *恢复修改: 输入“[Ctrl]r”. 这里,和撤销命令相反,是撤销的撤销,也可多步。 *复制行到剪切板: 输入“yy”. *复制选定内容到剪切板: (1)输入“v”。 (2)按方向键将高亮选择的内容。 (3)输入“y”。 这里,开始输入v使vi临时进入了一个"选择模式",输入方向键可以选择,输入y将选择的内容复制剪切板。 *删除行: 输入“dd”. 注意,vi的删除等价于剪切,删除的内容会保存到剪切板中。 *删除选定内容: (1)输入“v”。 (2)按方向键将高亮选择的内容。 (3)输入“d”。 这里,开始输入v使vi临时进入了一个“选择模式”,输入方向键可以选择,输入d将选择的内容删除。 *粘贴: 输入“p”. 这样会将剪切板的内容粘贴到光标位置或者光标下一行。 *查找字符并定位到第一个匹配处: 输入“/character”. 这里character是待查找的字符,只要先输入/,再输入待查字符,最后回车即可定位到第一个匹配的字符处。 *定位到匹配查找的下一个字符处: 输入“n”. *定位到匹配查找的上一个字符处: 输入“N”. *保存文件: 输入“:w”. 注意w前面的':',输入':'之后,vim会将':'之后的输入解释为待执行的指令。 *退出: 输入“:q”. 这里,如果文件没有保存,将提示无法退出,除非你强制退出,不保存文件,或者保存退出。 *强制退出: 输入“:q!”. *保存退出: 输入“:wq”. 或输入“ZZ”. *察看帮助 输入":help". ** 另外还有一个简易的教程: 输入命令:vimtutor.

87,910

社区成员

发帖
与我相关
我的任务
社区描述
Web 开发 JavaScript
社区管理员
  • JavaScript
  • 无·法
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧