<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          nodejs 如何實(shí)現(xiàn)控制臺(tái)打印高亮代碼

          共 4205字,需瀏覽 9分鐘

           ·

          2021-05-29 21:19

          當(dāng)代碼運(yùn)行報(bào)錯(cuò)時(shí),我們會(huì)打印錯(cuò)誤,錯(cuò)誤中有堆棧信息,可以定位到對(duì)應(yīng)的代碼位置。但有的時(shí)候我們希望能夠更直接準(zhǔn)確的打印報(bào)錯(cuò)位置的代碼。比如這樣:

          這個(gè)可以使用 @babel/code-frames 來(lái)做到:

          const { codeFrameColumns } = require('@babel/code-frame');

          const res = codeFrameColumns(code, {
            start: { line2column1 },
            end: { line3column5 },
          }, {
            highlightCodetrue,
            message'這里出錯(cuò)了'
          });

          console.log(res);

          有沒(méi)有感覺(jué)比較神奇,它是怎么做到的打印出上面的代碼格式的(code frame)?

          本文我們就來(lái)探究下原理。

          主要會(huì)解答三個(gè)問(wèn)題:

          • 如何打印出標(biāo)記相應(yīng)位置代碼的 code frame(就是上圖的打印格式)
          • 如何實(shí)現(xiàn)語(yǔ)法高亮
          • 如何在控制臺(tái)打印顏色

          如何打印 code frame

          我們先不管高亮,實(shí)現(xiàn)這樣的格式的打印:

          有啥思路沒(méi)?

          其實(shí)也比較容易想到,傳入了源代碼、標(biāo)記開(kāi)始和結(jié)束的行列號(hào),那么我們就能夠計(jì)算出顯示標(biāo)記(marker)的行是哪些,以及這些行的哪些列,然后依次對(duì)每一行代碼做處理,如果本行沒(méi)有標(biāo)記則保持原樣,如果本行有標(biāo)記的話(huà),那么就在開(kāi)始打印一個(gè) marker “>”,并且在下面打印一行 marker "^",最后一個(gè)標(biāo)記行還要打印錯(cuò)誤信息。

          我們來(lái)看一下 @babel/code-frame 的實(shí)現(xiàn):

          首先,分割字符串成每一行的數(shù)組,然后根據(jù)傳入的位置計(jì)算出 marker 所在的位置。

          比如圖中第二行的第 1 到 12 列,第三行的 0 到 5 列。

          image.png

          然后對(duì)每一行做處理,如果本行有標(biāo)記,則拼成 marker + gutter(行號(hào)) + 代碼的格式,下面再打印一行 marker,最后的 marker 行打印 message。沒(méi)有標(biāo)記不處理。

          image.png

          這樣最終拼出的就是這樣的 code frame:

          我們實(shí)現(xiàn)了 code frame 的拼接,暫時(shí)忽略了高亮,那么怎么做語(yǔ)法高亮呢?

          如何實(shí)現(xiàn)語(yǔ)法高亮

          實(shí)現(xiàn)語(yǔ)法高亮需要理解代碼,但是如果只是高亮,詞法分析就足夠了。babel 也是這么做的,@babel/highlight 包里面完成了高亮代碼的邏輯。

          先看效果:

          const a = 1;
          const b = 2;
          console.log(a + b);

          上面的源碼被分成了 token 數(shù)組:

          [
            [ 'whitespace''\n' ], [ 'keyword''const' ],
            [ 'whitespace'' ' ],  [ 'name''a' ],
            [ 'whitespace'' ' ],  [ 'punctuator''=' ],
            [ 'whitespace'' ' ],  [ 'number''1' ],
            [ 'punctuator'';' ],  [ 'whitespace''\n' ],
            [ 'keyword''const' ], [ 'whitespace'' ' ],
            [ 'name''b' ],        [ 'whitespace'' ' ],
            [ 'punctuator''=' ],  [ 'whitespace'' ' ],
            [ 'number''2' ],      [ 'punctuator'';' ],
            [ 'whitespace''\n' ], [ 'name''console' ],
            [ 'punctuator''.' ],  [ 'name''log' ],
            [ 'bracket''(' ],     [ 'name''a' ],
            [ 'whitespace'' ' ],  [ 'punctuator''+' ],
            [ 'whitespace'' ' ],  [ 'name''b' ],
            [ 'bracket'')' ],     [ 'punctuator'';' ],
            [ 'whitespace''\n' ]
          ]

          token 怎么分的呢?

          一般來(lái)說(shuō)詞法分析就是有限狀態(tài)自動(dòng)機(jī)(DFA),但是這里實(shí)現(xiàn)比較簡(jiǎn)單,是通過(guò)正則匹配的:

          js-tokens 這個(gè)包暴露出來(lái)一個(gè)正則,一個(gè)函數(shù),正則是用來(lái)識(shí)別 token 的,其中有很多個(gè)分組,而函數(shù)里面是對(duì)不同的分組下標(biāo)返回了不同的類(lèi)型,這樣就能完成 token 的識(shí)別和分類(lèi)。

          在 @babel/highlight 包里也是這樣用的:

          image.png

          (正則來(lái)做詞法分析有很多限制條件,比如不能處理遞歸的情況,所以這種方式不是通用的,通用詞法分析還是得用狀態(tài)機(jī) DFA。)

          有了分類(lèi)之后,不同 token 顯示不同顏色,建立個(gè) map 就行了。

          @babel/highlight 也是這么做的:

          我們知道了怎么做語(yǔ)法高亮,使用 chalk 的 api 就可以打印顏色,那控制臺(tái)打印顏色的原理是什么呢?

          如何在控制臺(tái)打印顏色

          控制臺(tái)打印的是 ASCII 碼,并不是所有的編碼都對(duì)應(yīng)可見(jiàn)字符,ASCII 碼有一部分字符是對(duì)應(yīng)控制字符的,比如 27 是 ESC,就是我們鍵盤(pán)上的 ESC 鍵,是 escape 的縮寫(xiě),按下它可以完成一些控制功能,這里我們可以通過(guò)打印 ESC 的 ASCII 碼來(lái)進(jìn)入控制打印顏色的狀態(tài)。

          格式是這樣的:

          打印一個(gè) ESC 的 ASCII 碼,之后是 [ 代表開(kāi)始,m 代表結(jié)束,中間是用 ; 分隔的 n 個(gè)控制字符,可以控制很多樣式,比如前景色、背景色、加粗、下劃線(xiàn)等等。

          ESC 的 ASCII 碼是 27,有好幾種寫(xiě)法:一種是字符表示的 \e ,一種是 16 進(jìn)制的 \0x1b(27 對(duì)應(yīng)的 16進(jìn)制),一種是 8 進(jìn)制的 \033,這三種都表示 ESC。

          我們來(lái)試驗(yàn)一下:1 表示加粗、36 表示前景色為青色、4 表示下劃線(xiàn),下面三種寫(xiě)法等價(jià):

          \e[36;1;4m
          \033[36;1;4m
          \0x1b[36;1;4m

          我們來(lái)試一下:

          都打印了正確的樣式!

          當(dāng)然,加了樣式還要去掉,可以加一個(gè) \e[0m 就可以了(\033[0m,\0x1b[0m 等價(jià))。

          chalk(nodejs 的在終端打印顏色的庫(kù))的不同方法就是封裝了這些 ASCII 碼的顏色控制字符。

          上面每行代碼被高亮過(guò)以后的代碼是:

          image.png

          這樣也就實(shí)現(xiàn)了不同顏色的打印。

          總結(jié)

          至此,我們能實(shí)現(xiàn)開(kāi)頭的效果了:支持 code frame 的打印,支持語(yǔ)法高亮,能夠打印顏色

          本文我們探究了這種效果的實(shí)現(xiàn)原理,先從 code frame 是怎么拼接的,然后每一行的代碼是怎么做高亮的,之后是高亮具體是怎么打印顏色的。

          不管是 code frame 的打印,還是語(yǔ)法高亮或者控制臺(tái)打印顏色,都是特別常見(jiàn)的功能,希望這篇文章能夠幫你徹底掌握這 3 方面的原理。


          瀏覽 34
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  俺去也亚洲 | 亚洲最大在线观看 | 黄片无码在线免费观看 | 尹人网大香蕉 | 三级精品在线 |