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


  • 相关阅读:
    Charles的使用教程
    Sublime Text 3 无法使用package control安装插件解决办法
    sublime常用快捷键整理(未完待续)
    常用 Git 命令清单
    js判断的执行顺序
    使用CSS3 Media Queries实现网页自适应(转)
    移动前端开发之viewport的深入理解(转)
    javascript之函数节流
    String常用方法总结
    腾讯面试题1
  • 原文地址:https://www.cnblogs.com/sanshi/p/1539636.html
Copyright © 2020-2023  润新知