在写新的模式(mode)之前,还是先来概括一下CodeMirror的设计思路。CodeMirror分为三部分:核心,模式,扩展。核心库对外开放了很多接口,而这些接口就是实现新模式或者新扩展的基石。

在使用CodeMirror的过程中,如果我们需要的mode不在CodeMirror自带的Modes里面,就需要我们自定义mode,比如ini文件类型,CodeMirror自身并没有实现,这时候就需要设计新的mode。

顺便提一下mode和language的区别,mode和language并不是完全一一对应,比如html代码中,往往包含javascript 和css代码,对于混杂着javascript和css代码的html文档,我们抽象成一个htmlmixed模式,就是所谓的“多重模式”,而 javascript和css也有自身对于的mode。

多重模式的实现对单一模式是有依赖的,这也体现了CodeMirror作者良好了设计功底,代码尽量得到最大效率的利用,htmlmixed模式内部就是"javascript","css","xml"三种模式的轮询。

大致原理:

拿典型的javascript文件来说,要完成javascript模式,只要定义一个词法分析程序就可以,首先,CodeMirror核心程序已经将整个javascript文档按照行做了拆分,你在mode里所需要做的就是写一个词法分析程序分析每一行数据。

对于单行的字符串文本,词法分析程序需要从字符串开始分析到字符串结束,在这过程中,会对需要标识的字符或者字符串(比如[ ] {} (),this,function等)做特殊标识,并且返回一个css样式(用于高亮)给对应被标识的字符或者字符串,这就是大概的处理过程。更高级的用 法还可以控制代码文档的缩进等。

注册模式:

在模式的实现脚本中,首先要调用CodeMirror.defineMode接口注册模式,这个接口函数包含两个参数:

第一个参数类型为字符串,是模式的名称,一律使用小写,一般情况下,注册的模式名称保持跟模式脚本名称一样,比如xml模式的实现脚本为"xml.js",注册的模式名就使用"xml"。

第二个参数类型为函数,该函数也有两个入参:分别是CodeMirror的配置对象(传递给CodeMirror构造函数使用,参见配置一节)和mode的配置对象(包含哪些属性由具体的mode决定)。该函数的返回值是mode对象,具体内容后面会提到。

代码约束:

在编写Mode代码的时候,所有代码都要控制在与调用CodeMirror.defineMode接口的同一作用域内,所有函数变量的申明都要写在CodeMirror.defineMode第二个参数(函数)内部。这样可以避免mode变量对全局变量造成污染。

状态对象

Mode的主要作用就是对行文本进行词法分析进而进行文本标识(高亮),当然了主要功能也是基础功能,大部分mode的设计要求远远不止对文本进行高亮,同时,某种语言的复杂程度也影响着mode的复杂程度。

有些规则很简洁明确的语言,可能每行的词法分析都是跟上一行没有关系的,比如ini文件,它的注释,块,键值肯定在同一行内,不可能跨行,词法分析函数关起门来潜心练就九阴真经就行了。

不幸的是,大多数语言的规则都是比较复杂的,就拿javascript来讲,一个数组可以在行内写,也可以跨行写,如果跨行写的话,那么后一行的词法分析肯定就会对前一行的文本有依赖,要不然无法进行正确的分析。

鉴于这个原因,CodeMirror设计了状态对象(state Object),该对象是唯一的,也可以认为是每一行共享的(类似全局变量),它的属性在词法分析函数内部做维护,如此一来不同行之前就有了联系,词法分 析函数在分析当前行文本的时候,可以从状态对象获取到自己感兴趣的信息。

在需要使用状态对象的Modes内部,mode对象必须定义一个startState属性,该属性是一个函数,函数返回一个对象,这个对象便是状态 对象,在开始对整个文档进行分析之前,CodeMirror就会利用startState生成一个状态对象,而这个状态对象里面有哪些属性,完全是根据 mode的设计要求和思路来决定的。

词法分析函数

在mode的设计中,mode对象的词法分析函数(token(stream,state))是最重要的,所有的mode都要定义这个方法。
首先token函数有两个入参:

stream:CodeMirror核心内部定义的Stream类的实例,它不光包含当前行的文本信息,还包含已经分析到的字符位置(字符串分析的时候,是一个个字符顺序分析的),也包含一系列使用的字符串处理方法;

state:就是状态对象,每次词法分析,都会传入这个参数,然后在词法分析内部维护这个参数的属性。

在分析同一行文本的时候,token函数会被反复调用,只要是遇到一个需要标识的子字符串片段或者空白字符,token都会return,直到分析到整行结尾,看个例子,有以下一行javascript代码:

var name = this.name;

一、 token从字符串第一个字符开始分析,此时stream.pos为0,遇到一个关键字"var",则返回一个css样式"keyword",同时 stream.pos移动到2,CodeMirror将会给var外面加上span标签,该标签的class就是"cm-kerword"(被核心模块加 上了"cm-"前缀,token函数返回的所有样式都会被加上这个前缀),cm-kerword样式定义在codemirror.css文件中。

二、 字符串还没有到结束,第二次调用token分析,遇到一个空格,返回null,也就是说空格不需要高亮,同时stream.pos移动到3;

三、字符串还没有到结束,第三次调用token分析,遇到一个属性name,返回"properties",同时stream.pos移动到7,CodeMirror将会给name外面加上span标签,该标签的class是"cm-properties"。

四、字符串还没有到结束,继续调用token,直到分析完这个字符串才会结束。

其实词法分析的概要过程就是这样的,是不是很简单呢?具体实现的细节其实还是比较繁琐的。

样式化

样式化是在词法分析过程中完成的,也是词法分析的目的,CodeMirror支持好几种样式化的方式,都是通过特殊关键字来区别的

1. 普通样式,不包含"line-"和"line-background-"前缀的都是普通样式,作用在整行字符串的某个词汇或子字符串上。

2. 带有"line-"前缀的样式,作用在整行文字上,在DOM结构里,该样式是加在pre标签上的。需要注意的是在整行字符串分析的过程过,只要有一次返回值包含"line-"前缀,pre标签就会被加上对应样式,多次返回不同值,则会增加多个样式。

3. 带有"line-background-"前缀的样式作用整行文字的背景,该样式会创建一个跟pre平行的块元素,其z-index小于pre标 签,pre自身的背景被设置为透明,所以新创建的块元素模拟了整行文本的背景色。跟"line-"前缀一样,多次返回便是增加多个样式

4. token还可以返回多重样式(使用空格做分割),例如返回"string error",则是字符串样式(着色)和Error(划线)样式的叠加。

PS:codemirror.css文件没有的样式都需要自行定义,一般自定义的样式文件写在跟mode脚本文件同目录同名。

看个被标识好的整行DOM结构或许会更清楚:

    <!-- 整个一行的DOM结构 -->
<div class="" style="position: relative;">
<!-- 只有token函数返回带有line-background-关键字的样式,这个节点才会被创建 -->
<!-- 本示例token返回值包含line-background-linebgc -->
<!-- CodeMirror-linebackground样式定义在codemirror.css中,linebgc则需要自己定义-->
<div class="linebgc CodeMirror-linebackground"></div>
<!-- 行号对应的DOM 不在token的作用范围内 -->
<div class="CodeMirror-gutter-wrapper" style="position: absolute; left: -29px;">
<div class="CodeMirror-linenumber CodeMirror-gutter-elt" style="left: 0px; width: 20px;">3</div>
</div>
<!-- 正式的整行文本对应的DOM,该标签的背景被CodeMirror设置为透明 -->
<!-- pre标签被增加了"linetext"样式,是因为token返回值中包含了"line-linetext",有"line-"前缀,所以生成了这个样式 -->
<!-- linetext 样式需要自行定义 -->
<pre class="linetext ">
<span style="padding-right: 0.1px;">
<!-- token返回值包含key -->
<span class="cm-key">innodb_log_file_size</span>
<!-- token返回值包含"equal-symbol strong" 是一个多重样式 -->
<span class="cm-equal-symbol cm-strong">=</span>
<!-- token返回值包含"vaule" -->
<span class="cm-value">10M</span>
<!-- token返回值包含"comment" -->
<span class="cm-comment">;ddad</span>
</span>
</pre>
</div>

编写CodeMirror Modes详解的更多相关文章

  1. Vue插件编写、用法详解(附demo)

    Vue插件编写.用法详解(附demo) 1.概述 简单来说,插件就是指对Vue的功能的增强或补充. 比如说,让你在每个单页面的组件里,都可以调用某个方法,或者共享使用某个变量,或者在某个方法之前执行一 ...

  2. 如何编写JQuery 插件详解

    转载自:http://blog.sina.com.cn/s/blog_6154bf970101jam7.html 如今做web开发,jquery 几乎是必不可少的,就连vs神器在2010版本开始将Jq ...

  3. 如何为开发项目编写规范的README文件(windows),此文详解

    为什么要写这篇博客? 其实我是一个入坑已经半年的程序员,因为不是计算机专业,只能自己摸索,所以我深知博客的重要性.每次我的学习笔记啊,项目的,面试题啊,有的,只要有时间,我肯定上传上来,一方面自己可以 ...

  4. 前端html、CSS快速编写代码插件-Emmet使用方法技巧详解

    前端html.CSS快速编写代码插件-Emmet使用方法技巧详解   Emmet的前身是大名鼎鼎的Zen coding,如果你从事Web前端开发的话,对该插件一定不会陌生.它使用仿CSS选择器的语法来 ...

  5. HBase 协处理器编程详解,第二部分:客户端代码编写

    实现 Client 端代码 HBase 提供了客户端 Java 包 org.apache.hadoop.hbase.client.coprocessor.它提供以下三种方法来调用协处理器提供的服务: ...

  6. nand flash详解及驱动编写

    https://www.crifan.com/files/doc/docbook/linux_nand_driver/release/html/linux_nand_driver.html#nand_ ...

  7. [转帖]helm模板文件chart编写语法详解

    helm模板文件chart编写语法详解 https://blog.51cto.com/qujunorz/2421328 需要学习一下. charts编写介绍 开始 快速创建一个chart模板,helm ...

  8. python编写微信公众号首图思路详解

    前言 之前一直在美图秀秀调整自己的微信公众号首图,效果也不尽如人意,老是调来调去,最后发出来的图片被裁剪了一大部分,丢失部分关键信息,十分恼火,于是想着用python写一个程序,把微信公众号首图的模式 ...

  9. Linux dts 设备树详解(二) 动手编写设备树dts

    Linux dts 设备树详解(一) 基础知识 Linux dts 设备树详解(二) 动手编写设备树dts 文章目录 前言 硬件结构 设备树dts文件 前言 在简单了解概念之后,我们可以开始尝试写一个 ...

随机推荐

  1. 百度云语音识别,Audio2Txt(c#)

    百度云识别没有提供c#版本的sdk,下面给个c#的 1.打开网址http://developer.baidu.com/ 2.登陆 3.管理控制台>开发者服务管理 4.创建工程 5.输入名称,点击 ...

  2. JavaScript修改Canvas图片

    用JavaScript修改Canvas图片的分辨率(DPI)   应用场景: 仓库每次发货需要打印标签, Canvas根据从数据库读取的产品信息可以生成标签JPG, 但是这个JPG图片的默认分辨率(D ...

  3. Lazy<T>在Entity Framework中的性能优化实践

    Lazy<T>在Entity Framework中的性能优化实践(附源码) 2013-10-27 18:12 by JustRun, 328 阅读, 4 评论, 收藏, 编辑 在使用EF的 ...

  4. [转]Resolving kernel symbols

    原:http://ho.ax/posts/2012/02/resolving-kernel-symbols/ KXLD doesn't like us much. He has KPIs to mee ...

  5. 搜索广告与广告网络Demand技术-探索与利用

    探索与利用(Explore and exploit) 点击率预测中还有一个重要的问题,就是探索与利用,它在工程中解决的并不好,我这章把现在论文中的常见的几种方法介绍一下.探索与利用它是所有互联网应用都 ...

  6. LLVM小结

    随笔- 5  文章- 0  评论- 10  LLVM小结   如果说gcc是FSF的传奇,llvm就是Chris Lattner的小清新.当然啦,想具体看看这位四处游山玩水还GPA 4.0的大神和他的 ...

  7. JS、JQuery和ExtJs动态创建DOM对象

    做了个简单使用JavaScript.JQuery.ExtJs进行DOM对象创建的测试,主要是使用JavaScript.JQuery.ExtJs动态创建Table对象.动态Table数据填充.多选控制. ...

  8. MongoDB安装心得

    本人纯前端一枚,对于数据库安装各种纠结,出了不少错误,一一列出,方便遇到同样问题的人给以参考,也加深一下自己印象. 故事开始了...Node.js在前端界比较火,由于我也是小小前端,跟随大潮流开始步入 ...

  9. 微软必应借PK谷歌突围中国搜索市场

    Bing“必应”是微软2009年推出的搜索品牌(http://www.bing.com),它取代的是同门师兄Live Search.进入2013年,在国内及好莱坞的多部大片里面,我都看到了Bing搜索 ...

  10. 学习新手给Android新手的一些学习建议

    时间紧张,先记一笔,后续优化与完善. Shamoo做Android开辟已有一年了,对Android开辟也有一点点了解.上面就给Android新手说说我对Android浅面的意识和一点建议吧,知道的大牛 ...