CodeMirrorjs代码加亮使用的示例分析-创新互联

小编给大家分享一下CodeMirror js代码加亮使用的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

坚守“ 做人真诚 · 做事靠谱 · 口碑至上 · 高效敬业 ”的价值观,专业网站建设服务10余年为成都成都铜雕雕塑小微创业公司专业提供成都企业网站建设营销网站建设商城网站建设手机网站建设小程序网站建设网站改版,从内容策划、视觉设计、底层架构、网页布局、功能开发迭代于一体的高端网站建设服务。

CodeMirror是一个基于JavaScript的代码编辑器,CodeMirror支持大量语言的语法高亮,也包括css,html,js等的高亮显示。此外,CodeMirror还支持代码自动完成、搜索/替换、HTML预览、行号、选择/搜索结果高亮、可视化tab、代码自动格式等。

CodeMirror源码的github地址:https://github.com/marijnh/CodeMirror/。这几天除了上课之外有空我都是在啃着它的源码,在网上相关资料基本一点都没找到,发觉看起来真是很吃力,这篇总结也只是说个大概原理,具体细节我也很多不明白,虽然很多代码都读得懂,但是串联起来有很大问题,没注释,源码大部分变量都是猜它的意思,大部分函数也真是知道个大概实现什么功能。  

CodeMirror之所以能够支持这么多语言的高亮,是由于在它的mode包中定义了多种语言的解析方式,然后对外提供统一的接口。源码中也把这部分内容分为一个层次。下面我主要是对CodeMirror库自带的对JS和CSS代码加亮脚本为例进行了研究。

这个是它定义的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来初始化一个状态物体,而这个状态物体具体包含什么内容完全由具体应用决定,CodeMirror没有硬性规定。
(2)token:这是最主要的解析语法函数,通过调用state.tokenize(stream,state)执行 function jsTokenBase(stream, state) {...},下面我会解析这个函数的主要内容.
(3)indent:这个是可有可无的

说下jsTokenBase这个函数,通过stream.next()读取下一个字符,并对字符进行判断,主要用到了正则匹配,返回的结果???

function jsTokenBase(stream,state){
  var ch = stream.next();
  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 if(isOperatorChar.test(ch)) return ret(“operator”); //返回操作符
  else { stream.eatWhile(/[\w\$_]/); return ..} //返回匹配字符串 
}

上面这个只是判断每一个ch = stream.next() 是属于什么类型的字符,也就是知道现在的字符是属于符号,字符串,数字,注释还是其他的.
接着,更重点的还是后面的字符串栈,其实在代码里面是可以看到栈的影子的。就像编译原理里面的语法分析和语义分析,你需要扫描字符串中的每个字符,并判断是否进栈或者规约,这学期的编译原理没特别认真去学,还得重新复习一遍。在前面举例子时其实就已经感受到,加亮JS或CSS代码需要上下文,而JS或CSS的大括号、冒号这种层级关系从上往下从左往右读时恰好是一个压栈的过程。

For example:
  function pushcontext(){...}
  function popcontext() {...}
  function pushlex(type,info){..}
  function poplex(){...}

然后通过function statement(type){}等进行调用。

另外要说的一点是,上面判断中为什么需要标记这么多的状态?因为高亮并不是一次性完成的,当用户完输入代码后,可能会将光标移动到任意一个点,然后修改代码,这时难道要重新解析整个代码吗?不是,但是某种程度上来说也是。是,因为用户修改点之后的代码必须重新高亮,因为用户可能输入一个大括号,从而改变所有之后代码的层级(一个大括号入栈,之后的代码的栈环境均发生改变,而加亮方案要靠栈的元素决定)。也不是。因为之前的代码当然可以很安全地认为是不需要重新加亮的,所以如果重新加亮整个代码是没必要的,试想若是几千行的代码,用户每次按键都要重新加亮,岂不是非常低效。所以,当每次捕获加亮任务,程序应该从这个修改点往后进行加亮。而实际上CodeMirror也是这么做的。这个多状态物体,就是为了能很快的重新从某个点开始重新加亮。CodeMirror其实会帮你“备份”这些状态物体(copystate函数),对于源代码中的copyState函数实现细节还真是不懂....

相比JS的mode文件,CSS会感觉简单点,容易理解点..原理也差不多,就不多说一遍了,总体上是定义大堆的keyword,然后对于每个关键符号进行判断,也用到了stack.
github源码:https://github.com/marijnh/CodeMirror/blob/master/mode/css/css.js

现在转到CodeMirror的主函数,html的调用方式为:

var editor = CodeMirror.fromTextArea(document.getElementById("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) 再者通过 mode.token(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 js代码加亮使用的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注创新互联网站建设公司行业资讯频道!

另外有需要云服务器可以了解下创新互联建站www.cdcxhl.com,海内外云服务器15元起步,三天无理由+7*72小时售后在线,公司持有idc许可证,提供“云服务器、裸金属服务器、高防服务器、香港服务器、美国服务器、虚拟主机、免备案服务器”等云主机租用服务以及企业上云的综合解决方案,具有“安全稳定、简单易用、服务可用性高、性价比高”等特点与优势,专为企业上云打造定制,能够满足用户丰富、多元化的应用场景需求。


当前文章:CodeMirrorjs代码加亮使用的示例分析-创新互联
转载来于:http://myzitong.com/article/dgicpj.html