這篇文章是對 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, "<").replace(/\>/g, ">"); 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+(?= *(?:<) *(?:\w+|\?)(?:(?:<)|(?:>)|,|\w| |\.)* *(?:>)(?: *\[ *\])? +\w+ *[;=,\(\)])/)) { text = text.replace(/\w+(?= *(?:<) *(?:\w+|\?)(?:(?:<)|(?:>)|,|\w| |\.)* *(?:>)(?: *\[ *\])? +\w+ *[;=,\(\)])/, "###genericl_{" + genericsl.length + "}###"); genericsl.push(tgenericl[0]); } //暫時排除 new 關鍵字的泛型實例化 var newgenerics = new Array(); var tnewgeneric = null; while (tnewgeneric = text.match(/new +[\w]+ *(?:<) *\w*(?:(?:<)|(?:>)|,|\w| |\.)* *(?:>) *\(/)) { text = text.replace(/new +[\w]+ *(?:<) *\w*(?:(?:<)|(?:>)|,|\w| |\.)* *(?:>) *\(/, "###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+(?:<)\w+(?:>) *\()/)) { text = text.replace(/\w+(?= +\w+(?:<)\w+(?:>) *\()/, "###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+(?=(?: *<))/)) { coloredText = "<strong style=\"color:" + colorObj.classColor + ";\">" + tnewgeneric[0] + "</strong>"; originText = originText.replace(/\w+(?=(?: *<))/, coloredText); } t_regex = new RegExp("###newgeneric_\\{" + i + "\\}###"); text = text.replace(t_regex, originText); } //暫時排除單對泛型括號中的類型 var agbrackets = new Array(); var tagbrackets = null; while (tagbrackets = text.match(/< *[\.,\w ]+ *>/)) { text = text.replace(/< *[\.,\w ]+ *>/, "###agbrackets_{" + agbrackets.length + "}###"); agbrackets.push(tagbrackets[0]); } //還原並着色單對泛型括號中的類型 for (var i = 0; i < agbrackets.length; i++) { var leftText = agbrackets[i].substr(0, agbrackets[i].match(/<\s*/)[0].length); var rightText = agbrackets[i].substr(agbrackets[i].search(/>\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