編寫CodeMirror Modes詳解

來源:互聯網
上載者:User

標籤:

在寫新的模式(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詳解

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.