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

05 代码片格式化

2015-10-22 21:25 381 查看
前言

这是一个之前没事的时候的一个想法, 想想他们博客的代码的显示格式化的问题, 然后 在网上找了一下资源, 恰好有一个网友也做了, 然后就下载了, 参考了一下, 但是原来作者是使用正则替换各个关键字的显示, 注释等等 […我有点看不懂], 所以后来自己想了一下, 不过当你第一映像想到的东西几乎是最简单[最容易想到的]的实现方法, 所以 这里的实现也算是一个普通的算法吧

主要知识点 : 按照给定的多个分隔符进行分词, 以及拼接html, 以及特殊字符的替换 [空格, <, > 等等]

需要输入的数据如下, keys [关键字 -> 样式class的映射], seps [分隔符, 用于分词], singleLineAnno [单行注释], multiLineAnno [多上注释 [开始注释 -> 结束注释的映射] ]

请注意 : singleLineAnno 以及multiLineAnno中的符号必需出现在seps中, 否则 无法执行注释的判断逻辑

如果你需要添加一种语言的”渲染”

1. 现在formatCodeSegment.html页面添加 < select id=”lang” > 标签下面添加一个类似于< option value =”java”>Java< /option>的标签

2. 在formatCodeSegment.js脚本中添加添加上面的四个配置, 然后更新一下initKeysAndSeps, 添加入你添加的样式对于核心业务逻辑的”keys, seps, singleLineAnno, multiLineAnno”的配置即可

主要的思路如下 : 对于一行数据, 将其按照seps进行分词, 首先判断当前分隔符

1) 如果当前multiLineAnnoEle为空, 即当前行不在多行注释的范围内

1 如果当前分割符为”“” 或者”’”, 即双引号 或者单引号, 则获取下一个匹配的引号的位置, 然后将引号以及之间的内容添加为引号的样式块[< span class=’quote /singleQuote’ >]

2 如果当前分割符为单行注释符号[通常有 “//”, “#”, “;”等等], 各个语言更具自己的情况进行配置singleLineAnno 将当前行当前分隔符之后的数据作为注释快添加为注释的样式块[< span class=’conmment’ >]

3 如果当前当前分割符为多行注释符号[通常有 “/, /”, “{, }” 等等]各个语言更具自己的情况进行配置multiLineAnno,

尝试在当前行获取注释的结束符号 如果存在, 则将注释符号之间的部分添加为注释块[< span class=’conmment’ >]

如果当前行不存在注释结束符, 则设置multiLineAnnoEle为开始的”多行注释的符号”, 然后递归当前方法进入if(multiLineAnnoEle != null)判定, 执行解决多行注释的逻辑, 将结果添加为注释快[< span class=’conmment’ >]

4 剩余的就只有普通的单词, 以及关键字单词了, 分别处理 如果是关键字单词, 添加该单词对应的关键字的样式[keys 中配置]

2) 如果当前multiLineAnnoEle不为空, 即当前行在多行注释的范围内

1. 判定当前行是否有multiLineAnnoEle对应的注释结束符

如果存在 则将到注释结束符之前的内容均添加为注释块样式, 并设置multiLineAnnoEle为null, 解析注释结束符之后的数据 [之后进入的是”multiLineAnnoEle==null”的分支]

否则 将当前行添加为注释快样式

分词的图示 :



核心代码

1 formatCodeSegment.html : 前台显示代码

<body>
<textarea id="code" class='inputTextArea' oninput="format()" ></textarea>
<select id="lang" onchange="format()" >
<option value ="java">Java</option>
<option value ="c++">C++</option>
<option value="c">C</option>
<option value="scala">Scala</option>
</select>

<br/><br/>
预览效果:       <button onclick="copyToClipBoard()">一键复制</button>
<span id="result"></span>

</body>


2 getCodeKeyAndSeps.js : 生成控制着管理”keys, seps, singleLineAnno, multiLineAnno” 配置的逻辑

// 初始化逻辑需要使用的keys, seps [根据语言不通, 而不通]
// 如果想要添加其他语言, 请接着if ... else if  配置
function initKeysAndSeps(lang) {
if(equalsIgnoreCase("java", lang) ) {
keys = javaKeys
seps = javaSeps
singleLineAnno = javaSingleLineAnno
multiLineAnno = javaMultiLineAnno
} else if(equalsIgnoreCase("c++", lang) ) {
keys = cppKeys
seps = cppSeps
singleLineAnno = cppSingleLineAnno
multiLineAnno = cppMultiLineAnno
} else if(equalsIgnoreCase("c", lang) ) {
keys = cKeys
seps = cSeps
singleLineAnno = cSingleLineAnno
multiLineAnno = cMultiLineAnno
}  else if(equalsIgnoreCase("scala", lang) ) {
keys = scalaKeys
seps = scalaSeps
singleLineAnno = scalaSingleLineAnno
multiLineAnno = scalaMultiLineAnno
}
}


4 formatCodeSegment.js : js实现的StringBuilder

// js实现StringBuilder
function StringBuilder() {
this._stringArray = new Array();
}
StringBuilder.prototype.append = function(str){
this._stringArray.push(str);
}
StringBuilder.prototype.toString = function(sep){
return this._stringArray.join(sep);
}


3 formatCodeSegment.js : 控制着核心”渲染”逻辑, 根据每一行数据, 进行样式化数据

// 格式化给定的输入
// 遍历每一行, 进行格式化
// 并且更新li颜色间隔显示, 以及鼠标高亮显示
function format() {
var codeRows = document.getElementById("code").value.split("\n")
var result = document.getElementById("result")
initKeysAndSeps(document.getElementById("lang").value)

var resultContent = new StringBuilder()
resultContent.append(olCodeStart)
for(i in codeRows) {
resultContent.append(liCodeStart)
var row = formatOneRow(codeRows[i], copyOfArray(seps) )
resultContent.append(row)
resultContent.append(liCodeEnd)
}
resultContent.append(olCodeEnd)

// log(resultContent.toString("") )
result.innerHTML = resultContent.toString("")
liColor()
}

var multiLineAnnoEle = null
// 格式化一行代码
// 分词, 格式化数据
function formatOneRow(row, seps) {
if(multiLineAnnoEle != null) {
var formatted = new StringBuilder()
var endComment = multiLineAnno[multiLineAnnoEle]
var endCommentIdx = row.indexOf(endComment)
if(endCommentIdx >= 0) {
appendComment(formatted, row.substring(0, endCommentIdx + endComment.length) )
formatted.append(formatOneRow(row.substr(endCommentIdx + endComment.length), seps) )
multiLineAnnoEle = null
} else {
appendComment(formatted, row)
}

return formatted.toString("");
} else {
var formatted = new StringBuilder()
var copyOfSeps = copyOfArray(seps)
var sepsMap = {}
for(i in seps) {
sepsMap[seps[i]] = -1
}
updateSeps(sepsMap, seps, row, 0)

// lastMinIdx 控制着是否需要更新上一个轮询的最小的分隔符
var lastMinIdx = -1
var lastIdx = 0
while(getLength(sepsMap) > 0) {
if(lastMinIdx != -1) {
sepsMap[seps[lastMinIdx]] = row.indexOf(seps[lastMinIdx], lastIdx)
if(sepsMap[seps[lastMinIdx]] < 0) {
delete sepsMap[seps[lastMinIdx]]
delete seps[lastMinIdx]
}
}
if(getLength(sepsMap) == 0) {
break
}

lastMinIdx = getMinIdx(sepsMap, seps)
// log(lastIdx + " -> " + sepsMap[seps[lastMinIdx]])
var word = row.substring(lastIdx, sepsMap[seps[lastMinIdx]])
lastIdx = sepsMap[seps[lastMinIdx]] + seps[lastMinIdx].length

// 至此 word表示当前分割符的前一个字符串, seps[lastMinIdx]表示当前分隔符
// 判断各个分隔符特有的样式 [比如 这里的单引号, 双引号]
if(seps[lastMinIdx] == "\"") {
appendWord(formatted, word)
var nextQuote = row.indexOf(seps[lastMinIdx], lastIdx) + seps[lastMinIdx].length
appendQuote(formatted, row.substring(sepsMap[seps[lastMinIdx]], nextQuote) )
updateSeps(sepsMap, seps, row, nextQuote)
lastIdx = nextQuote
lastMinIdx = -1
} else if (seps[lastMinIdx] == "'") {
appendWord(formatted, word)
var nextQuote = row.indexOf(seps[lastMinIdx], lastIdx) + seps[lastMinIdx].length
appendSingleQuote(formatted, row.substring(sepsMap[seps[lastMinIdx]], nextQuote) )
updateSeps(sepsMap, seps, row, nextQuote)
lastIdx = nextQuote
lastMinIdx = -1
} else if(containsEle(singleLineAnno, seps[lastMinIdx]) )  {
appendWord(formatted, word)
appendComment(formatted, row.substr(sepsMap[seps[lastMinIdx]]) )
return formatted.toString("")
} else if(multiLineAnno[seps[lastMinIdx]] != null) {
multiLineAnnoEle = seps[lastMinIdx];
appendComment(formatted, word)
formatted.append(formatOneRow(row.substr(seps[lastMinIdx]) ))
return formatted.toString("")
} else {
appendWord(formatted, word)
appendSep(formatted, seps[lastMinIdx])
}
}
// 处理最后一个元素
var word = row.substr(lastIdx)
appendWord(formatted, word)

return formatted.toString("")
}
}

// 更新li行的颜色
function liColor() {
tabNode = document.getElementById("result").getElementsByTagName("ol")[0];
trs = tabNode.getElementsByTagName("li");

for(var i=0; i<trs.length; i++)
{
if(i&1 == 1){
trs[i].className += " oddLi";
} else {
trs[i].className += " evenLi";
}

trs[i].onmouseover = function() {
this.className += " overLi";
}

// 不能使用"-=", 因为"-" 并不为字符串连接符
trs[i].onmouseout = function() {
this.className = this.className.substring(0, this.className.indexOf("overLi") )
}
}
}

// ---------知识点----------
// 判断给定的key是否在字典中存在
// console.log(keys["sdf"] == null)

// 字符串化给定的json对象
// var sepsMap = {"sdf":"sdf"}
// console.log(JSON.stringify(sepsMap) )

// 字符串化给定的json对象
// var sepsMap = JSON.parse('{"sdf":"sdf"}'')
// console.log(sepsMap )

// 删除给定的元素
// delete sepsMap["ele"]

// for..in  如果集合是数组, 则迭代的是索引, 否则 集合中的元素

// ---------!知识点----------


效果截图 :

因为在浏览器中点击’ 或者”, 就导致了浏览器当前页面不响应了, 所以 这里演示以”`”代替了”“”

敲一段代码 :



语言模式的切换 :



参考 : http://download.csdn.net/detail/cawarey/4295065

下载链接 [包含图片, 源码] :

http://download.csdn.net/detail/u011039332/9204535



注 : 因为作者的水平有限,必然可能出现一些bug, 所以请大家指出!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  格式化 代码 博客