1. 程式人生 > 程式設計 >nodejs控制檯列印高亮程式碼的實現方法

nodejs控制檯列印高亮程式碼的實現方法

前言

當代碼執行報錯時,我們會列印錯誤,錯誤中有堆疊資訊,可以定位到對應的程式碼位置。但有的時候我們希望能夠更直接準確的列印報錯位置的程式碼。比如這樣:

nodejs控制檯列印高亮程式碼的實現方法

這個可以使用 @babel/code-frames 來做到:

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

const res = codeFrameColumns(code,{
  start: { line: 2,column: 1 },end: { line: 3,column: 5 },},{
  highlwww.cppcns.comightCode: true,message: '這裡出錯了'
});

console.log(res);

有沒有感覺比較神奇,它是怎麼做到的打印出上面的程式碼格式的(code frame)?

本文我們就來探究下原理。

主要會解答三個問題:

  • 如何打印出標記相應位置程式碼的 code frame(就是上圖的列印格式)
  • 如何實現語法高亮
  • 如何在控制檯列印顏色

如何列印 code frame

我們先不管高亮,實現這樣的格式的列印:

nodejs控制檯列印高亮程式碼的實現方法

有啥思路沒?

其實也比較容易想到,傳入了原始碼、標記開始和結束的行列號,那麼我們就能夠計算出顯示標記(marker)的行是哪些,以及這些行的哪些列,然後依次對每一行程式碼做處理,如果本行沒有標記則保持原樣,如果本行有標記的話,那麼就在開始列印一個 marker “>”,並且在下面列印一行 marker "^",最後一個標記行還要列印錯誤資訊。

我們來看一下 @babel/code-frame 的實現:

首先,分割字串成每一行的陣列,然後根據傳入的位置計算出 marker 所在的位置。

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

nodejs控制檯列印高亮程式碼的實現方法

然後對每一行做處理,如果本行有標記,則拼成 marker + gutter(行號) + 程式碼的格式,下面再列印一行 marker,最後的 marker 行列印 message。程式設計客棧沒有標記不處理。

nodejs控制檯列印高亮程式碼的實現方法

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

nodejs控制檯列印高亮程式碼的實現方法

我們實現了 code frame 的拼接,暫時忽略了高亮,那麼怎麼做語法高亮呢?

如何實現語法高亮

實現語法高亮需要理解程式碼,但是如果只是高亮,詞法分析就足夠了。babel 也是這麼做的,@babel/highlight 包裡面完成了高亮程式碼的邏輯。

先看效果:

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

上面的原始碼被分成了 token 陣列:

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

token 怎麼分的呢?

一般來說詞法分析就是有限狀態自動機(DFA),但是這裡實現比較簡單,是通過正則匹配的:

js-tokens 這個包暴露出來一個正則,一個函式,正則是用來識別 token 的,其中有很多個分組,而函式裡面是對不同的分組下標返回了不同的型別,這樣就能完成 token 的識別和分類。

nodejs控制檯列印高亮程式碼的實現方法

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

nodejs控制檯列印高亮程式碼的實現方法

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

有了分類之後,不同 token 顯示不同顏色,建立個 map 就行了。

@babel/highlight 也是這麼做的:

nodejs控制檯列印高亮程式碼的實現方法

我們知道了怎麼做語法高亮,使用 chalk 的 api 就可以列印顏色,那控制檯列印顏色的原理是什麼呢?

如何在控制檯列印顏色

控制檯列印的是 ASCII 碼,並不是所有的編碼都對應可見字元,ASCII 碼有一部分字元是對應控制字元的,比如 27 是 ESC,就是我們鍵盤上的 ESC 鍵,是 escape 的縮寫,按下它可以完成一些控制功能,這裡我們可以通過列印 ESC 的 ASCII 碼來進入控制列印顏色的狀態。

格式是這樣的:

nodejs控制檯列印高亮程式碼的實現方法

列印一個 ESC 的 ASCII 碼,之後是 [ 代表開始,m http://www.cppcns.com代表結束,中間是用 ; 分隔的 n 個控制字元,可以控制很多樣式,比如前景色、背景色、加粗、下劃線等等。

ESC 的 ASCII 碼是 27,有好幾種寫法:一種是字元表示的 \e ,一種是 16 進位制的 \0x1b(27 對應的 16進位制),一種是 8 進位制的 \033,這三種都表示 ESC。

我們來試驗一下:1 表示加粗、36 表示前景色為青色、4 表示下劃線,下面三種寫http://www.cppcns.com法等價:

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

我們來試一下:

nodejs控制檯列印高亮程式碼的實現方法

圖片都列印了正確的樣式!

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

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

上面每行程式碼被高亮過以後的程式碼是:

nodejs控制檯列印高亮程式碼的實現方法

這樣也就實現了不同顏色的列印。

總結

至此,我們能實現開頭的效果了:支援 code frame 的列印,支援語法高亮,能夠列印顏色

nodejs控制檯列印高亮程式碼的實現方法

本文我們探究了這種效果的實現原理,先從 code frame 是怎麼拼接的,然後每一行的程式碼是怎麼做高亮的,之後是高亮具體是怎麼列印顏色的。

不管是 code frame 的列印,還是語法高亮或者控制檯列印顏色,都是特別常見的功能,希望這篇文章能夠幫你徹底掌握這 3 方面的原理。

到此這篇關於nodejs控制檯列印高亮程式碼的文章就介紹到這了,更多相關nodejs控制檯列印高亮內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!