国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

CodeMirror js代碼加亮使用總結

2019-11-19 17:01:53
字體:
來源:轉載
供稿:網友

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 ..} //返回匹配字符串 }

上面這個只是判斷每一個ch = stream.next() 是屬于什么類型的字符,也就是知道現在的字符是屬于符號,字符串,數字,注釋還是其他的.
接著,更重點的還是后面的字符串棧,其實在代碼里面是可以看到棧的影子的。就像編譯原理里面的語法分析和語義分析,你需要掃描字符串中的每個字符,并判斷是否進?;蛘咭幖s,這學期的編譯原理沒特別認真去學,還得重新復習一遍。在前面舉例子時其實就已經感受到,加亮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, //是否自動換行});

其實調用過程中還可以傳遞更多自定義的參數,不過這里就不是討論重點了??傊前炎远x的屬性整合到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的應用可以參考:http://codemirror.net/

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 贞丰县| 栖霞市| 定远县| 宝山区| 醴陵市| 安新县| 霍山县| 塔城市| 上林县| 涞水县| 龙海市| 桂平市| 宜良县| 福贡县| 司法| 博野县| 剑河县| 垫江县| 平阴县| 呼玛县| 昔阳县| 林周县| 赣榆县| 昭通市| 洪泽县| 海南省| 花莲市| 芜湖市| 牙克石市| 珠海市| 米林县| 应用必备| 顺平县| 平泉县| 崇明县| 扶余县| 石嘴山市| 阿克陶县| 丰顺县| 八宿县| 邳州市|