溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

CodeMirror js代碼加亮使用的示例分析

發(fā)布時(shí)間:2021-08-02 13:42:31 來(lái)源:億速云 閱讀:420 作者:小新 欄目:web開(kāi)發(fā)

小編給大家分享一下CodeMirror js代碼加亮使用的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

CodeMirror是一個(gè)基于JavaScript的代碼編輯器,CodeMirror支持大量語(yǔ)言的語(yǔ)法高亮,也包括css,html,js等的高亮顯示。此外,CodeMirror還支持代碼自動(dòng)完成、搜索/替換、HTML預(yù)覽、行號(hào)、選擇/搜索結(jié)果高亮、可視化tab、代碼自動(dòng)格式等。

   CodeMirror源碼的github地址:https://github.com/marijnh/CodeMirror/。這幾天除了上課之外有空我都是在啃著它的源碼,在網(wǎng)上相關(guān)資料基本一點(diǎn)都沒(méi)找到,發(fā)覺(jué)看起來(lái)真是很吃力,這篇總結(jié)也只是說(shuō)個(gè)大概原理,具體細(xì)節(jié)我也很多不明白,雖然很多代碼都讀得懂,但是串聯(lián)起來(lái)有很大問(wèn)題,沒(méi)注釋,源碼大部分變量都是猜它的意思,大部分函數(shù)也真是知道個(gè)大概實(shí)現(xiàn)什么功能?! ?/p>

   CodeMirror之所以能夠支持這么多語(yǔ)言的高亮,是由于在它的mode包中定義了多種語(yǔ)言的解析方式,然后對(duì)外提供統(tǒng)一的接口。源碼中也把這部分內(nèi)容分為一個(gè)層次。下面我主要是對(duì)CodeMirror庫(kù)自帶的對(duì)JS和CSS代碼加亮腳本為例進(jìn)行了研究。

這個(gè)是它定義的js解析方式,下面我用mode.js代替該js文件
mode.js中主要定義了兩個(gè)函數(shù):

CodeMirror.defineMode("javascript",function(config,parserConfig){}
CodeMirror.defineMIME("text/javascript", "javascript");

這兩個(gè)define的作用主要是掛靠到CodeMirror這個(gè)主體類中

mode.js 對(duì)外提供的接口主要是:

return{
  startState:function(basecolumn){...}
  token:function(stream,state){...}
  indent:function(state,textAfter){...}      
}

現(xiàn)在解析這三個(gè)函數(shù):

(1)startState:主要是定義函數(shù)解析執(zhí)行的上下文環(huán)境,起始的狀態(tài),如果沒(méi)有這個(gè)方法的話,相當(dāng)于在解析過(guò)程中沒(méi)有了語(yǔ)義。
startState鍵雖然不是必選但也十分重要,因?yàn)楦吡镣婕罢Z(yǔ)境,即目前高亮的短語(yǔ)處于一個(gè)什么樣的上下文中,通常影響語(yǔ)義和顏色的選取。所以需要一個(gè)startState來(lái)初始化一個(gè)狀態(tài)物體,而這個(gè)狀態(tài)物體具體包含什么內(nèi)容完全由具體應(yīng)用決定,CodeMirror沒(méi)有硬性規(guī)定。
(2)token:這是最主要的解析語(yǔ)法函數(shù),通過(guò)調(diào)用state.tokenize(stream,state)執(zhí)行 function jsTokenBase(stream, state) {...},下面我會(huì)解析這個(gè)函數(shù)的主要內(nèi)容.
(3)indent:這個(gè)是可有可無(wú)的

說(shuō)下jsTokenBase這個(gè)函數(shù),通過(guò)stream.next()讀取下一個(gè)字符,并對(duì)字符進(jìn)行判斷,主要用到了正則匹配,返回的結(jié)果???

function jsTokenBase(stream,state){
  var ch = stream.next();
  if(ch == '”' || ch=”'”)
    return ...;          //判斷是否存在下個(gè)”或',return [“string”,”string”]
  else if(/[\[\]{}\(\),;\:\.]/.test(ch))
    return .. ;          //匹配[]{}()...這幾個(gè),return ch
  else if(ch==”0” && stream.eat(/x/i)){
    stream.eatwhile(/[\da-f]/i); //0x**,解析16進(jìn)制數(shù)
    return ret(“number”,”number”);//返回一個(gè)自己封裝好的對(duì)象function ret(tp,style,cont)
  }
  else if(/\d/.test(ch) || ch ==“”&&stream.eat(/\d/)) 
    return ret(“number”,”number”);//匹配數(shù)字
  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”]; //返回語(yǔ)句是錯(cuò)誤的
  else if(isOperatorChar.test(ch)) return ret(“operator”); //返回操作符
  else { stream.eatWhile(/[\w\$_]/); return ..} //返回匹配字符串 
}

上面這個(gè)只是判斷每一個(gè)ch = stream.next() 是屬于什么類型的字符,也就是知道現(xiàn)在的字符是屬于符號(hào),字符串,數(shù)字,注釋還是其他的.
接著,更重點(diǎn)的還是后面的字符串棧,其實(shí)在代碼里面是可以看到棧的影子的。就像編譯原理里面的語(yǔ)法分析和語(yǔ)義分析,你需要掃描字符串中的每個(gè)字符,并判斷是否進(jìn)棧或者規(guī)約,這學(xué)期的編譯原理沒(méi)特別認(rèn)真去學(xué),還得重新復(fù)習(xí)一遍。在前面舉例子時(shí)其實(shí)就已經(jīng)感受到,加亮JS或CSS代碼需要上下文,而JS或CSS的大括號(hào)、冒號(hào)這種層級(jí)關(guān)系從上往下從左往右讀時(shí)恰好是一個(gè)壓棧的過(guò)程。

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

然后通過(guò)function statement(type){}等進(jìn)行調(diào)用。

另外要說(shuō)的一點(diǎn)是,上面判斷中為什么需要標(biāo)記這么多的狀態(tài)?因?yàn)楦吡敛⒉皇且淮涡酝瓿傻模?dāng)用戶完輸入代碼后,可能會(huì)將光標(biāo)移動(dòng)到任意一個(gè)點(diǎn),然后修改代碼,這時(shí)難道要重新解析整個(gè)代碼嗎?不是,但是某種程度上來(lái)說(shuō)也是。是,因?yàn)橛脩粜薷狞c(diǎn)之后的代碼必須重新高亮,因?yàn)橛脩艨赡茌斎胍粋€(gè)大括號(hào),從而改變所有之后代碼的層級(jí)(一個(gè)大括號(hào)入棧,之后的代碼的棧環(huán)境均發(fā)生改變,而加亮方案要靠棧的元素決定)。也不是。因?yàn)橹暗拇a當(dāng)然可以很安全地認(rèn)為是不需要重新加亮的,所以如果重新加亮整個(gè)代碼是沒(méi)必要的,試想若是幾千行的代碼,用戶每次按鍵都要重新加亮,豈不是非常低效。所以,當(dāng)每次捕獲加亮任務(wù),程序應(yīng)該從這個(gè)修改點(diǎn)往后進(jìn)行加亮。而實(shí)際上CodeMirror也是這么做的。這個(gè)多狀態(tài)物體,就是為了能很快的重新從某個(gè)點(diǎn)開(kāi)始重新加亮。CodeMirror其實(shí)會(huì)幫你“備份”這些狀態(tài)物體(copystate函數(shù)),對(duì)于源代碼中的copyState函數(shù)實(shí)現(xiàn)細(xì)節(jié)還真是不懂....

相比JS的mode文件,CSS會(huì)感覺(jué)簡(jiǎn)單點(diǎn),容易理解點(diǎn)..原理也差不多,就不多說(shuō)一遍了,總體上是定義大堆的keyword,然后對(duì)于每個(gè)關(guān)鍵符號(hào)進(jìn)行判斷,也用到了stack.
github源碼:https://github.com/marijnh/CodeMirror/blob/master/mode/css/css.js

現(xiàn)在轉(zhuǎn)到CodeMirror的主函數(shù),html的調(diào)用方式為:

var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
  mode: "application/xml",
  styleActiveLine: true, //line選擇是是否加亮
  lineNumbers: true, //是否顯示行數(shù)
  lineWrapping: true, //是否自動(dòng)換行
});

其實(shí)調(diào)用過(guò)程中還可以傳遞更多自定義的參數(shù),不過(guò)這里就不是討論重點(diǎn)了??傊前炎远x的屬性整合到CodeMirror的defaultConfig中。
在CodeMirror里通過(guò)functionhighlightLine(cm,line,state){}調(diào)用 function runMode(cm,text,mode,state,f) 再者通過(guò) mode.token(stream,state) 調(diào)用mode.js對(duì)外公開(kāi)的接口token。
在hightlightLine()函數(shù)執(zhí)行前進(jìn)行了大量的配置定義和分行然后格式化對(duì)應(yīng)的字符串.剩下部分前兩天看了不過(guò)現(xiàn)在還真得再看一遍才能理清楚思路啦,幾千行的代碼,用最笨的方法看..

如果簡(jiǎn)單的詞語(yǔ)高亮,而且不需要考慮到很復(fù)雜的語(yǔ)義,用正則表達(dá)式可以簡(jiǎn)單解決..如:

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)"), //匹配關(guān)鍵字
kw2 = new RegExp("(\\/\\/[^\n<]*(?:\n|$))(?!<\\/)"), //匹配注釋

但是正則有時(shí)候也會(huì)出很多問(wèn)題,在有語(yǔ)義的情況下,寫(xiě)正則表達(dá)式是很麻煩的.高亮一般的方式是采用編譯原理里面的語(yǔ)法分析+語(yǔ)義分析,這部分是有點(diǎn)難度的。本來(lái)還想在CodeMirror基礎(chǔ)上改進(jìn)些東西,但是發(fā)現(xiàn)很難,還不如自己寫(xiě)個(gè)簡(jiǎn)單的,過(guò)些天有空我會(huì)自己嘗試下。

以上是“CodeMirror js代碼加亮使用的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對(duì)大家有所幫助,如果還想學(xué)習(xí)更多知識(shí),歡迎關(guān)注億速云行業(yè)資訊頻道!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI