为了账号安全,请及时绑定邮箱和手机立即绑定

dp.SyntaxHighlighter代码分析

标签:
架构


文章截图 - 更好的排版
源代码下载

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()));    });});



点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消