CodeMirrorjs代码加亮⼩⼩总结
CodeMirror是⼀个基于JavaScript的代码编辑器,CodeMirror⽀持⼤量语⾔的语法⾼亮,也包括css,html,js等的⾼亮显⽰。此外,CodeMirror还⽀持代码⾃动完成、搜索/替换、HTML预览、⾏号、选
择/搜索结果⾼亮、可视化tab、代码⾃动格式等。
CodeMirror之所以能够⽀持这么多语⾔的⾼亮,是由于在它的mode包中定义了多种语⾔的解析⽅式,然后对外提供统⼀的接⼝。源码中也把这部分内容分为⼀个层次。下⾯我主要是
对CodeMirror库⾃带的对JS和CSS代码加亮脚本为例进⾏了研究。
github:
这个是它定义的js解析⽅式,下⾯我⽤mode.js代替该js⽂件
mode.js中主要定义了两个函数:
CodeMirror.defineMode("javascript",function(config,parserConfig){}
CodeMirror.defineMIME("text/javascript", "javascript");
这两个define的作⽤主要是挂靠到CodeMirror这个主体类中
mode.js 对外提供的接⼝主要是:
return{
startState:function(basecolumn){...}
token:function(stream,state){...}
indent:function(state,textAfter){...}
}
现在解析这三个函数:
(1)startState:主要是定义函数解析执⾏的上下⽂环境,起始的状态,如果没有这个⽅法的话,相当于在解析过程中没有了语义。
startState键虽然不是必选但也⼗分重要,因为⾼亮往往涉及语境,即⽬前⾼亮的短语处于⼀个什么样的上下⽂中,通常影响语义和颜⾊的选取。所以需要⼀个startState来初始化⼀个状态物体,⽽这个状
态物体具体包含什么内容完全(2)token:这是最主要的解析语法函数,通过调⽤kenize(stream,state)执⾏function jsTokenBase(stream, state) {...},下⾯我会解析这个函数的主要内容.
(3)indent:这个是可有可⽆的
说下jsTokenBase这个函数,通过()读取下⼀个字符,并对字符进⾏判断,主要⽤到了正则匹配,返回的结果
function jsTokenBase(stream,state){
var ch = ();
if(ch == '”' || ch=”'”)
return ...; //判断是否存在下个”或',return [“string”,”string”]
else if(/[\[\]{}\(\),;\:\.]/.test(ch))
return .. ; //匹配[]{}()...这⼏个,return ch
else if(ch==”0” && stream.eat(/x/i)){
stream.eatwhile(/[\da-f]/i); //0x**,解析16进制数
return ret(“number”,”number”);//返回⼀个⾃⼰封装好的对象function ret(tp,style,cont)
}
else if(/\d/.test(ch) || ch ==“”&&stream.eat(/\d/))
return ret(“number”,”number”);//匹配数字
else if (ch == "/") { //匹配注释
if(stream.eat(“*”)) return [“comment”,”comment”]; //判断“/*”
else if(stream.eat(“/”)) return [“comment”,”comment”]; //判断“//”
else if (state.lastType == "operator" || state.lastType == "keyword c" || /^[\[{}\(,;:]$/.test(state.lastType)) {} //??
else if(stream.eatWhile(isOperatorChar)) return ret(“operator”); //判断/之后的操作符
}
else if(ch == "#") return [“error”,”error”]; //返回语句是错误的
else st(ch)) return ret(“operator”); //返回操作符
js正则表达式判断数字else { stream.eatWhile(/[\w\$_]/); return ..} //返回匹配字符串
}
上⾯这个只是判断每⼀个ch = () 是属于什么类型的字符,也就是知道现在的字符是属于符号,字符串,数字,注释还是其他的.
接着,更重点的还是后⾯的字符串栈,其实在代码⾥⾯是可以看到栈的影⼦的。就像编译原理⾥⾯的语法分析和语义分析,你需要扫描字符串中的每个字符,并判断是否进栈或者规约,这学期的编译原理没特别认真去学,还得重新For example:
function pushcontext(){...}
function popcontext() {...}
function pushlex(type,info){..}
function poplex(){...}
然后通过function statement(type){}等进⾏调⽤。
另外要说的⼀点是,上⾯判断中为什么需要标记这么多的状态?因为⾼亮并不是⼀次性完成的,当⽤户完输⼊代码后,可能会将光标移动到任意⼀个点,然后修改代码,这时难道要重新解析整个代码吗?不是,但是某种程度上来说相⽐JS的mode⽂件,CSS会感觉简单点,容易理解点..原理也差不多,就不多说⼀遍了,总体上是定义⼤堆的keyword,然后对于每个关键符号进⾏判断,也⽤到了stack.
github源码:
现在转到CodeMirror的主函数,html的调⽤⽅式为:
var editor = CodeMirror.ElementById("code"), {
mode: "application/xml",
styleActiveLine: true, //line选择是是否加亮
lineNumbers: true, //是否显⽰⾏数
lineWrapping: true, //是否⾃动换⾏
});
其实调⽤过程中还可以传递更多⾃定义的参数,不过这⾥就不是讨论重点了。总之是把⾃定义的属性整合到CodeMirror的defaultConfig中。
在CodeMirror⾥通过functionhighlightLine(cm,line,state){}调⽤function runMode(cm,text,mode,state,f) 再者通过ken(stream,state) 调⽤mode.js对外公开的接⼝token。
在hightlightLine()函数执⾏前进⾏了⼤量的配置定义和分⾏然后格式化对应的字符串.剩下部分前两天看了不过现在还真得再看⼀遍才能理清楚思路啦,⼏千⾏的代码,⽤最笨的⽅法看..
如果简单的词语⾼亮,⽽且不需要考虑到很复杂的语义,⽤正则表达式可以简单解决..如:
var kw1 = new RegExp("(if|while|with|else|do|try|finally|return|break|continue|new|delete|throw|var|function|catch|for|switch|case|default|typeof|instanceof|true|false|null|undefined|NaN)"), //匹配关键字
kw2 = new RegExp("(\\/\\/[^\n<]*(?:\n|$))(?!<\\/)"), //匹配注释
但是正则有时候也会出很多问题,在有语义的情况下,写正则表达式是很⿇烦的.⾼亮⼀般的⽅式是采⽤编译原理⾥⾯的语法分析+语义分析,这部分是有点难度的。本来还想在CodeMirror基础上改进些东西,但是发现附:
CodeMirror的应⽤可以参考:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论