CodeMirror是一個基于JavaScript的代碼編輯器,CodeMirror支持大量語言的語法高亮,也包括css,html,js等的高亮顯示。此外,CodeMirror還支持代碼自動完成、搜索/替換、HTML預覽、行號、選擇/搜索結果高亮、可視化tab、代碼自動格式等。
CodeMirror源碼的github地址:https://github.com/marijnh/CodeMirror/。這幾天除了上課之外有空我都是在啃著它的源碼,在網上相關資料基本一點都沒找到,發覺看起來真是很吃力,這篇總結也只是說個大概原理,具體細節我也很多不明白,雖然很多代碼都讀得懂,但是串聯起來有很大問題,沒注釋,源碼大部分變量都是猜它的意思,大部分函數也真是知道個大概實現什么功能。
CodeMirror之所以能夠支持這么多語言的高亮,是由于在它的mode包中定義了多種語言的解析方式,然后對外提供統一的接口。源碼中也把這部分內容分為一個層次。下面我主要是對CodeMirror庫自帶的對JS和CSS代碼加亮腳本為例進行了研究。
github:https://github.com/marijnh/CodeMirror/blob/master/mode/javascript/javascript.js
這個是它定義的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 ..} //返回匹配字符串 }
新聞熱點
疑難解答
圖片精選