您的位置:首页 > 编程语言

dp.SyntaxHighlighter代码分析

2013-06-24 11:29 225 查看
文章截图 - 更好的排版
源代码下载

dp.SyntaxHighlighter代码分析

dp.SyntaxHighlighter作为一个最常用的JavaScript代码高亮工具受到广泛的欢迎。
那么你有没有想知道它的内部实现机制是什么,本文在对其分析后,抽取核心代码以重现其功能。
注:本文中的代码高亮使用的就是手工打造的dp.SyntaxHighlighter的简化版。

核心思想
1. 首先定义了一系列的正则表达式用来匹配所有的高亮代码,包括关键字、单行注释、多行注释、字符串, 如果你仔细观察高亮后的JavaScript代码,你会发现也只有这些代码被高亮显示了,是不是很简单。
var keywords = 'abstract boolean break byte case catch char class const continue debugger ' +
'default delete do double else enum export extends false final finally float ' +
'for function goto if implements import in instanceof int interface long native ' +
'new null package private protected public return short static super switch ' +
'synchronized this throw throws transient true try typeof var void volatile while with';
var regexList = [{ regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'gm'), css: 'comment' },
{ regex: new RegExp('//.*$', 'gm'), css: 'comment' },
{ regex: new RegExp('"(?:\\.|(\\\\\\")|[^\\""\\n])*"', 'g'), css: 'string' },
{ regex: new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'", 'g'), css: 'string' },
{ regex: new RegExp(getKeywords(keywords), 'gm'), css: 'keyword'}];
function getKeywords(str) {
return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
}


2. 接下来我们需要通过JavaScript中的正则匹配来找到所有的匹配项。
for (var i = 0; i < regexList.length; i++) {
var match = null;
while ((match = regexList[i].regex.exec(source)) != null) {
matchs.push({
value: match[0],
index: match.index,
length: match[0].length,
css: regexList[i].css
});
}
}


3. 然后对匹配项进行排序(按照其在原始代码中的顺序)
function sortCallback(m1, m2) {
if (m1.index < m2.index)
return -1;
else if (m1.index > m2.index)
return 1;
else {
return 0;
}
}
matchs = matchs.sort(sortCallback);


4. 最后拿着这些排好顺序的匹配项,和原始代码进行整合就好了。

注意事项

在拿到排序后的匹配项列表后,还不能和原始代码整合,因为其中有重复项,比如:
var keywords = 'abstract boolean break';
会找到如下匹配项:var,'abstract boolean break',abstract,boolean,break这五个匹配项,其中一个字符串,四个关键词。
对于这样的情况,我们还必须起初重复的匹配项。
function isInlcude(matchs, index) {
var match = matchs[index];
for (var i = index - 1; i >= 0; i--) {
if (matchs[i]) {
if (match.index > matchs[i].index && match.index < matchs[i].index + matchs[i].length) {
return true;
}
}
}
return false;
}
for (var i = 0; i < matchs.length; i++) {
if (isInlcude(matchs, i)) {
matchs[i] = null;
}
}


修改后的源代码

这里只处理JavaScript的情况,对于其他语言的代码,只需要定义不同的正则匹配表达式就行了。
function hightLight(source) {
var keywords = 'abstract boolean break byte case catch char class const continue debugger ' +
'default delete do double else enum export extends false final finally float ' +
'for function goto if implements import in instanceof int interface long native ' +
'new null package private protected public return short static super switch ' +
'synchronized this throw throws transient true try typeof var void volatile while with';
var regexList = [{ regex: new RegExp('/\\*[\\s\\S]*?\\*/', 'gm'), css: 'comment' },
{ regex: new RegExp('//.*$', 'gm'), css: 'comment' },
{ regex: new RegExp('"(?:\\.|(\\\\\\")|[^\\""\\n])*"', 'g'), css: 'string' },
{ regex: new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'", 'g'), css: 'string' },
{ regex: new RegExp(getKeywords(keywords), 'gm'), css: 'keyword'}];
var matchs = [], result = [];
function getKeywords(str) {
return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
}
function sortCallback(m1, m2) {
if (m1.index < m2.index)
return -1;
else if (m1.index > m2.index)
return 1;
else {
return 0;
}
}
function isInlcude(matchs, index) {
var match = matchs[index];
for (var i = index - 1; i >= 0; i--) {
if (matchs[i]) {
if (match.index > matchs[i].index && match.index < matchs[i].index + matchs[i].length) {
return true;
}
}
}
return false;
}
function convertSpace(str) {
return str.replace(/\t/g, " ").replace(/ /g, " ");
}
for (var i = 0; i < regexList.length; i++) { var match = null; while ((match = regexList[i].regex.exec(source)) != null) { matchs.push({ value: match[0], index: match.index, length: match[0].length, css: regexList[i].css }); } }if (matchs.length) {
matchs = matchs.sort(sortCallback);
// 去除重复
for (var i = 0; i < matchs.length; i++) {
if (isInlcude(matchs, i)) {
matchs[i] = null;
}
}
var i = 0;
for (var j = 0; j < matchs.length; j++) {
var match = matchs[j];
if (match) {
if (match.index > i) {
result.push('<span>' + convertSpace(source.substr(i, match.index - i)) + '</span>');
}
result.push('<span class="' + match.css + '">' + convertSpace(match.value) + '</span>');
i = match.index + match.length;
}
}
if (i != source.length - 1) {
result.push(convertSpace(source.substr(i)));
}
}
var resultLines = result.join("").split("\n");
var out = [], alt = false;
out.push('<div class="dp-highlighter"><ol start="1" class="dp-c">');
for (var i = 0; i < resultLines.length; i++) {
out.push(alt ? '<li class="alt">' : '<li>');
out.push('<span>');
out.push(resultLines[i] === "" ? " " : resultLines[i]);
out.push('</span>');
out.push('</li>');
alt = !alt;
}
out.push('</ol></div>');
return out.join("");
}
$(function() {
$("pre.js").each(function() {
var $this = $(this);
$this.hide().after(hightLight($this.html()));
});
});
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: