Javascript 實現對 Java 的語法高亮

這篇文章是對 Java 實現語法高亮的部分;我用 Java 的緣由是 Android App 開發須要,由於我用的是 Android Studio ,因此着色的風格是參考的 Android Studio 默認顏色;考慮到 Java 與 C# 屬於同等級別的編譯型高級語言,並且它們的語法較相似,因此能夠在以前 C# 高亮的基礎上實行修改:ide

//可用的語法着色數據
var Colors = {
    //Java 暗
    JavaDark: {
        "language": "Java",
        "theme": "dark",
        "classColor": "#FFC66D",
        "annotationColor": "#808080",
        "annotationsColor": "#808080",
        "stringColor": "#71A567",
        "constantColor": "#5295CB",
        "notesColor": "#BBB529",
        "keywordsInfo": [{
            "color": "#CB772F",
            "keywords": ["abstract", "assert", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "default", "do", "double", "else", "enum", "extends", "false", "final", "finally", "float", "for", "goto", "if", "implements", "import", "int", "instanceof", "interface", "long", "native", "new", "null", "object", "operator", "out", "override", "package", "private", "protected", "public", "return", "    strictfp", "super", "short", "switch", "synchronized", "static", "string", "struct", "switch", "this", "throw", "throws", "true", "try", "transient", "void", "volatile", "while"]
        }],
        "customClassInfo": [{
            "color": "#B8D7A3",
            "keywords": ["Calendar", "Color", "ForegroundColorSpan", "InputMethodManager", "Log", "PreferenceManager", "R", "Spanned", "String", "StyleSpan", "Typeface"]
        }]
    }
};
//使用某一種顏色數據對指定的文本進行語法着色(主入口)
function StainColor(text, colorObj) {
    if (colorObj.language == "Java") {
        return StainGrammerJava(text, colorObj);
    }
}
//Java 語法着色
function StainGrammerJava(text, colorObj) {
    text = text.replace(/&/g, "&");
    text = text.replace(/\</g, "&lt;").replace(/\>/g, "&gt;");
    var t_regex = null;
    var strings = new Array();
    var tstring = null;
    //暫時排除普通字符串
    while (tstring = text.match(/"(?:(?:\\")|[^"\r\n])*"/)) {
        text = text.replace(/"(?:(?:\\")|[^"\r\n])*"/, "###str_{" + strings.length + "}###");
        strings.push(tstring[0]);
    }
    //暫時排除多行註釋
    var comments = new Array();
    var tcomment = null;
    while (tcomment = text.match(/\/\*(?:.|\s)*?\*\//)) {
        text = text.replace(/\/\*(?:.|\s)*?\*\//, "###comment_{" + comments.length + "}###");
        comments.push(tcomment[0]);
    }
    //暫時排除單行註釋
    var annotations = new Array();
    var tannotation = null;
    while (tannotation = text.match(/\/\/.*/)) {
        text = text.replace(/\/\/.*/, "###annotation_{" + annotations.length + "}###");
        annotations.push(tannotation[0]);
    }
    //暫時排除單字符
    var chars = new Array();
    var tchar = null;
    while (tchar = text.match(/(?:'\\?[^\\]')|''/)) {
        text = text.replace(/(?:'\\?[^\\]')|''/, "###char_{" + chars.length + "}###");
        chars.push(tchar[0]);
    }
    //暫時排除@註解
    var notes = new Array();
    var tnote = null;
    while (tnote = text.match(/@ *\w+/)) {
        text = text.replace(/@ *\w+/, "###note_{" + notes.length + "}###");
        notes.push(tnote[0]);
    }
    //暫時排除常數
    var constants = new Array();
    var tconstant = null;
    while (tconstant = text.match(/\b\d+(?!\}#)\b/)) {
        text = text.replace(/\b\d+(?!\}#)\b/, "###constant_{" + constants.length + "}###");
        constants.push(tconstant[0]);
    }
    //暫時排除16進制常數
    while (tconstant = text.match(/\b0x(?:[0-9]|[a-f]|[A-F])+\b/)) {
        text = text.replace(/\b0x(?:[0-9]|[a-f]|[A-F])+\b/, "###constant_{" + constants.length + "}###");
        constants.push(tconstant[0]);
    }
    //暫時排除類聲明與定義部分
    var classes = new Array();
    var tclass = null;
    while (tclass = text.match(/class +\w+/)) {
        text = text.replace(/class +\w+/, "###class_{" + classes.length + "}###");
        classes.push(tclass[0]);
    }
    //暫時排除接口聲明與定義部分
    var interfaces = new Array();
    var tinterface = null;
    while (tinterface = text.match(/interface +\w+/)) {
        text = text.replace(/interface +\w+/, "###interface_{" + interfaces.length + "}###");
        interfaces.push(tinterface[0]);
    }
    //暫時排除異常捕獲的類型
    var exceptions = new Array();
    var texception = null;
    while (texception = text.match(/catch *\([ *\w\.]+(?=(?: +\w+))/)) {
        text = text.replace(/catch *\([ *\w\.]+(?=(?: +\w+))/, "###exception_{" + exceptions.length + "}###");
        exceptions.push(texception[0]);
    }
    //暫時排除類型聲明與初始化
    var types = new Array();
    var ttype = null;
    while (ttype = text.match(/\w+(?= *\?? *(?:\[\])? +\w+ *[;=,\)])/)) {
        text = text.replace(/\w+(?= *\?? *(?:\[\])? +\w+ *[;=,\)])/, "###type_{" + types.length + "}###");
        types.push(ttype[0]);
    }
    //暫時排除 new 關鍵字的類型實例化
    var newes = new Array();
    var tnew = null;
    while (tnew = text.match(/new +[\w\. ]+ *[\(\[]/)) {
        text = text.replace(/new +[\w\. ]+ *[\(\[]/, "###new_{" + newes.length + "}###");
        newes.push(tnew[0]);
    }
    //暫時排除泛型聲明與普通方法的泛型返回類型
    var genericsl = new Array();
    var tgenericl = null;
    while (tgenericl = text.match(/\w+(?= *(?:&lt;) *(?:\w+|\?)(?:(?:&lt;)|(?:&gt;)|,|\w| |\.)* *(?:&gt;)(?: *\[ *\])? +\w+ *[;=,\(\)])/)) {
        text = text.replace(/\w+(?= *(?:&lt;) *(?:\w+|\?)(?:(?:&lt;)|(?:&gt;)|,|\w| |\.)* *(?:&gt;)(?: *\[ *\])? +\w+ *[;=,\(\)])/, "###genericl_{" + genericsl.length + "}###");
        genericsl.push(tgenericl[0]);
    }
    //暫時排除 new 關鍵字的泛型實例化
    var newgenerics = new Array();
    var tnewgeneric = null;
    while (tnewgeneric = text.match(/new +[\w]+ *(?:&lt;) *\w*(?:(?:&lt;)|(?:&gt;)|,|\w| |\.)* *(?:&gt;) *\(/)) {
        text = text.replace(/new +[\w]+ *(?:&lt;) *\w*(?:(?:&lt;)|(?:&gt;)|,|\w| |\.)* *(?:&gt;) *\(/, "###newgeneric_{" + newgenerics.length + "}###");
        newgenerics.push(tnewgeneric[0]);
    }
    //暫時排除顯式類型轉換中的普通類型
    var converts = new Array();
    var tconvert = null;
    while (tconvert = text.match(/\( *\w+ *\) *\w+/)) {
        text = text.replace(/\( *\w+ *\) *\w+/, "###convert_{" + converts.length + "}###");
        converts.push(tconvert[0]);
    }
    //暫時排除方法的返回類型
    var methods = new Array();
    var tmethod = null;
    while (tmethod = text.match(/\w+(?=(?: *\[ *\])? +\w+ *\()/)) {
        text = text.replace(/\w+(?=(?: *\[ *\])? +\w+ *\()/, "###method_{" + methods.length + "}###");
        methods.push(tmethod[0]);
    }
    //暫時排除泛型方法的返回類型
    var gmethods = new Array();
    var tgmethod = null;
    while (tgmethod = text.match(/\w+(?= +\w+(?:&lt;)\w+(?:&gt;) *\()/)) {
        text = text.replace(/\w+(?= +\w+(?:&lt;)\w+(?:&gt;) *\()/, "###gmethod_{" + gmethods.length + "}###");
        gmethods.push(tgmethod[0]);
    }
    //還原並着色@註解
    for (var i = 0; i < notes.length; i++) {
        var coloredText = "<strong style=\"color:" + colorObj.notesColor + ";\">" + notes[i] + "</strong>";
        t_regex = new RegExp("###note_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色常數
    for (var i = 0; i < constants.length; i++) {
        var coloredText = "<strong style=\"color:" + colorObj.constantColor + ";\">" + constants[i] + "</strong>";
        t_regex = new RegExp("###constant_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色類聲明與定義部分
    for (var i = 0; i < classes.length; i++) {
        var leftText = classes[i].substr(0, classes[i].match(/class\s+/)[0].length);
        var keyword = classes[i].substr(leftText.length);
        var coloredText = leftText + "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>";
        t_regex = new RegExp("###class_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色接口聲明與定義部分
    for (var i = 0; i < interfaces.length; i++) {
        var leftText = interfaces[i].substr(0, interfaces[i].match(/interface\s+/)[0].length);
        var keyword = interfaces[i].substr(leftText.length);
        var coloredText = leftText + "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>";
        t_regex = new RegExp("###interface_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色異常捕獲的類型
    for (var i = 0; i < exceptions.length; i++) {
        var leftText = "";
        if (exceptions[i].indexOf(".") >= 0) {
            leftText = exceptions[i].substring(0, exceptions[i].lastIndexOf(".") + 1);
        } else {
            leftText = exceptions[i].substr(0, exceptions[i].match(/catch\s*\(\s*/)[0].length);
        }
        var keyword = exceptions[i].substr(leftText.length);
        var coloredText = leftText + "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>";
        t_regex = new RegExp("###exception_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色類型聲明與初始化
    for (var i = 0; i < types.length; i++) {
        var skip = false;
        for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
            if (colorObj.keywordsInfo[j].keywords.indexOf(types[i]) >= 0) {
                skip = true;
                break;
            }
        }
        //對關鍵字進行排除
        if (skip) {
            var originText = types[i];
            t_regex = new RegExp("###type_\\{" + i + "\\}###");
            text = text.replace(t_regex, originText);
        } else {
            var coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + types[i] + "</strong>";
            t_regex = new RegExp("###type_\\{" + i + "\\}###");
            text = text.replace(t_regex, coloredText);
        }
    }
    //還原並着色 new 關鍵字的類型實例化
    for (var i = 0; i < newes.length; i++) {
        var skip = false;
        for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
            t_regex = new RegExp("new +");
            var keyword = newes[i].replace(t_regex, "");
            if (keyword.indexOf(".") >= 0) {
                keyword = keyword.substr(keyword.indexOf(".") + 1);
            }
            keyword = keyword.substr(0, keyword.search(/[\(\[]/));
            if (colorObj.keywordsInfo[j].keywords.indexOf(keyword) >= 0) {
                skip = true;
                break;
            }
        }
        //對關鍵字進行排除
        if (skip) {
            var originText = newes[i];
            t_regex = new RegExp("###new_\\{" + i + "\\}###");
            text = text.replace(t_regex, originText);
        } else {
            var leftText = "", keyword = "";
            if (newes[i].indexOf(".") >= 0) {
                leftText = newes[i].substring(0, newes[i].lastIndexOf(".") + 1);
                keyword = newes[i].substring(newes[i].lastIndexOf(".") + 1, newes[i].search(/[\(\[]/));
            } else {
                leftText = newes[i].substring(0, newes[i].match(/new\s+/)[0].length);
                keyword = newes[i].substring(leftText.length + newes[i].lastIndexOf(".") + 1, newes[i].search(/[\(\[]/));
            }
            var rightText = newes[i].substring(newes[i].lastIndexOf(keyword) + keyword.length);
            var coloredText = leftText + "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>" + rightText;
            t_regex = new RegExp("###new_\\{" + i + "\\}###");
            text = text.replace(t_regex, coloredText);
        }
    }
    //還原並着色泛型聲明
    for (var i = 0; i < genericsl.length; i++) {
        var coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + genericsl[i] + "</strong>";
        t_regex = new RegExp("###genericl_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色 new 關鍵字的泛型實例化
    for (var i = 0; i < newgenerics.length; i++) {
        var originText = newgenerics[i];
        var coloredText = "";
        var tnewgeneric = null;
        while (tnewgeneric = originText.match(/\w+(?=(?: *&lt;))/)) {
            coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + tnewgeneric[0] + "</strong>";
            originText = originText.replace(/\w+(?=(?: *&lt;))/, coloredText);
        }
        t_regex = new RegExp("###newgeneric_\\{" + i + "\\}###");
        text = text.replace(t_regex, originText);
    }
    //暫時排除單對泛型括號中的類型
    var agbrackets = new Array();
    var tagbrackets = null;
    while (tagbrackets = text.match(/&lt; *[\.,\w ]+ *&gt;/)) {
        text = text.replace(/&lt; *[\.,\w ]+ *&gt;/, "###agbrackets_{" + agbrackets.length + "}###");
        agbrackets.push(tagbrackets[0]);
    }
    //還原並着色單對泛型括號中的類型
    for (var i = 0; i < agbrackets.length; i++) {
        var leftText = agbrackets[i].substr(0, agbrackets[i].match(/&lt;\s*/)[0].length);
        var rightText = agbrackets[i].substr(agbrackets[i].search(/&gt;\s*/));
        agbrackets[i] = agbrackets[i].replace(leftText, "").replace(rightText, "");
        var gtypes = agbrackets[i].split(",");
        var coloredText = "";
        for (var x = 0; x < gtypes.length; x++) {
            var skip = false;
            for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
                if (colorObj.keywordsInfo[j].keywords.indexOf(gtypes[x].trim()) >= 0) {
                    skip = true;
                    break;
                }
            }
            //對關鍵字進行排除
            if (skip) {
                coloredText += gtypes[x] + ",";
            } else {
                var keyword = "";
                if (gtypes[x].lastIndexOf(".") >= 0) {
                    var namespace = gtypes[x].substring(0, gtypes[x].lastIndexOf(".") + 1);
                    coloredText += namespace;
                    keyword = gtypes[x].substr(gtypes[x].lastIndexOf(".") + 1);
                } else {
                    keyword = gtypes[x];
                }
                coloredText += "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>,";
            }
        }
        coloredText = coloredText.substr(0, coloredText.length - 1);
        coloredText = leftText + coloredText + rightText;
        t_regex = new RegExp("###agbrackets_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色顯式類型轉換中的普通類型
    for (var i = 0; i < converts.length; i++) {
        var keyword = "", leftText = "", rightText = "";
        var skip = false;
        for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
            leftText = converts[i].substr(0, converts[i].match(/\(/)[0].length);
            keyword = converts[i].substring(leftText.length, converts[i].lastIndexOf(")"));
            rightText = converts[i].substr(converts[i].lastIndexOf(")"));
            if (colorObj.keywordsInfo[j].keywords.indexOf(keyword.replace(/\s/g, "")) >= 0) {
                skip = true;
                break;
            }
        }
        //對關鍵字進行排除
        if (skip) {
            var originText = converts[i];
            t_regex = new RegExp("###convert_\\{" + i + "\\}###");
            text = text.replace(t_regex, originText);
        } else {
            var coloredText = leftText + "<strong style=\"color:" + colorObj.classColor + ";\">" + keyword + "</strong>" + rightText;
            t_regex = new RegExp("###convert_\\{" + i + "\\}###");
            text = text.replace(t_regex, coloredText);
        }
    }
    //還原並着色方法的返回類型
    for (var i = 0; i < methods.length; i++) {
        var skip = false;
        for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
            if (colorObj.keywordsInfo[j].keywords.indexOf(methods[i]) >= 0) {
                skip = true;
                break;
            }
        }
        //對關鍵字進行排除
        if (skip) {
            var originText = methods[i];
            t_regex = new RegExp("###method_\\{" + i + "\\}###");
            text = text.replace(t_regex, originText);
        } else {
            var coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + methods[i] + "</strong>";
            t_regex = new RegExp("###method_\\{" + i + "\\}###");
            text = text.replace(t_regex, coloredText);
        }
    }
    //還原並着色泛型方法的返回類型
    for (var i = 0; i < gmethods.length; i++) {
        var skip = false;
        for (var j = 0; j < colorObj.keywordsInfo.length; j++) {
            if (colorObj.keywordsInfo[j].keywords.indexOf(gmethods[i]) >= 0) {
                skip = true;
                break;
            }
        }
        //對關鍵字進行排除
        if (skip) {
            var originText = gmethods[i];
            t_regex = new RegExp("###gmethod_\\{" + i + "\\}###");
            text = text.replace(t_regex, originText);
        } else {
            var coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + gmethods[i] + "</strong>";
            t_regex = new RegExp("###gmethod_\\{" + i + "\\}###");
            text = text.replace(t_regex, coloredText);
        }
    }
    //自定義額外類型着色(靜態類、枚舉等直接成員訪問)
    for (var i = 0; i < colorObj.customClassInfo.length; i++) {
        for (var j = 0; j < colorObj.customClassInfo[i].keywords.length; j++) {
            var tcustomClassInfo = null;
            var t_regex = new RegExp("(\\b" + colorObj.customClassInfo[i].keywords[j] + ")(?= *\\. *\\w+)", "g");
            var coloredText = "<strong style=\"color:" + colorObj.customClassInfo[i].color + ";\">" + colorObj.customClassInfo[i].keywords[j] + "</strong>";
            text = text.replace(t_regex, coloredText);
        }
    }
    //關鍵字着色
    for (var i = 0; i < colorObj.keywordsInfo.length; i++) {
        for (var j = 0; j < colorObj.keywordsInfo[i].keywords.length; j++) {
            t_regex = new RegExp("\\b" + colorObj.keywordsInfo[i].keywords[j] + "\\b", "g");
            var coloredText = "<strong style=\"color:" + colorObj.keywordsInfo[i].color + ";\">" + colorObj.keywordsInfo[i].keywords[j] + "</strong>";
            text = text.replace(t_regex, coloredText);
        }
    }
    //還原並着色非空字符串
    for (var i = 0; i < strings.length; i++) {
        var coloredText = "<strong style=\"color:" + colorObj.stringColor + ";\">" + strings[i] + "</strong>";
        t_regex = new RegExp("###str_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色非空字符
    for (var i = 0; i < chars.length; i++) {
        var coloredText = "<strong style=\"color:" + colorObj.stringColor + ";\">" + chars[i] + "</strong>";
        t_regex = new RegExp("###char_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色單行註釋
    for (var i = 0; i < annotations.length; i++) {
        //還原單行註釋中的字符串
        var t_string = null;
        while (t_string = annotations[i].match(/###str_\{(\d+)\}###/)) {
            annotations[i] = annotations[i].replace(/###str_\{(\d+)\}###/, strings[t_string[1]]);
        }
        //還原單行註釋中的多行註釋
        var t_comment = null;
        while (t_comment = annotations[i].match(/###comment_\{(\d+)\}###/)) {
            annotations[i] = annotations[i].replace(/###comment_\{(\d+)\}###/, comments[t_comment[1]]);
        }
        var coloredText = "<strong style=\"color:" + colorObj.annotationColor + ";\">" + annotations[i] + "</strong>";
        t_regex = new RegExp("###annotation_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    //還原並着色多行註釋
    for (var i = 0; i < comments.length; i++) {
        //還原多行註釋中的字符串
        var t_string = null;
        while (t_string = comments[i].match(/###str_\{(\d+)\}###/)) {
            comments[i] = comments[i].replace(/###str_\{(\d+)\}###/, strings[t_string[1]]);
        }
        var coloredText = "<strong style=\"color:" + colorObj.annotationsColor + ";\">" + comments[i] + "</strong>";
        t_regex = new RegExp("###comment_\\{" + i + "\\}###");
        text = text.replace(t_regex, coloredText);
    }
    return text;
}

總的來看,Java 彷佛不容許成員名以 @ 字符開頭,因此成員名相關的正則中能夠去掉對 @ 前綴的處理,一樣運算符重載和預編譯指令部分也如此;去掉不少部分後發現高亮的地方較少,因此就單獨再加了 16 進制常數和 @ 註解的高亮,這樣看上去就不至於太單調,大概效果以下:this

相關文章
相關標籤/搜索