這篇文章跟大家介紹一下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 }, }, { highlightCode: true, message: '这里出错了' }); console.log(res);
有沒有感覺比較神奇,它是怎麼做到的印出上面的程式碼格式的(code frame)?
本文我們就來探究下原理。
主要會解答三個問題:
我們先不管高亮,實現這樣的格式的列印:
有啥思路沒?
其實也比較容易想到,傳入了原始碼、標記開始和結束的行列號,那麼我們就能夠計算出顯示標記(marker)的行是哪些,以及這些行的哪些列,然後依序對每一行程式碼做處理,如果本行沒有標記則保持原樣,如果本行有標記的話,那麼就在開始打印一個marker “>”
,並且在下面打印一行marker "^"
,最後一個標記行也要列印錯誤訊息。
我們來看看@babel/code-frame 的實作:
首先,分割字串成每一行的陣列,然後根據傳入的位置計算出marker 所在的位置。
比如圖中第二行的第 1 到 12 列,第三行的 0 到 5 列。
然後對每一行做處理,如果本行有標記,則拼成marker gutter(行號) 程式碼的格式,下面再列印一行marker,最後的marker 行列印message。沒有標記不處理。
這樣最終拼出的就是這樣的code frame:
我們實作了code frame 的拼接,暫時忽略了高亮,那怎麼做語法高亮呢?
實現語法高亮需要理解程式碼,但是如果只是高亮,詞法分析就足夠了。 babel 也是這麼做的,@babel/highlight 包裡面完成了高亮程式碼的邏輯。
先看效果:
const a = 1; const b = 2; console.log(a + b);
上面的原始碼被分成了 token 陣列:
[ [ '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 怎麼分的呢?
一般來說詞法分析就是有限狀態自動機(DFA),但這裡實作比較簡單,是透過正規匹配的:
js-tokens 這個套件揭露出來一個正規則,一個函數,正規是用來識別token 的,其中有很多個分組,而函數裡面是對不同的分組下標返回了不同的類型,這樣就能完成token 的識別和分類。
在@babel/highlight 套件裡也是這樣用的:
(正規來做詞法分析有很多限制條件,例如不能處理遞歸的情況,所以這種方式不是通用的,通用詞法分析還是得用狀態機DFA。)
有了分類之後,不同token 顯示不同顏色,建立個map 就行了。
@babel/highlight 也是這麼做的:
#我們知道了怎麼做語法高亮,使用chalk 的api 就可以列印顏色,那控制台列印顏色的原理是什麼呢?
控制台打印的是 ASCII 码,并不是所有的编码都对应可见字符,ASCII 码有一部分字符是对应控制字符的,比如 27 是 ESC,就是我们键盘上的 ESC 键,是 escape 的缩写,按下它可以完成一些控制功能,这里我们可以通过打印 ESC 的 ASCII 码来进入控制打印颜色的状态。
格式是这样的:
打印一个 ESC
的 ASCII 码,之后是 [
代表开始,m
代表结束,中间是用 ;
分隔的 n 个控制字符,可以控制很多样式,比如前景色、背景色、加粗、下划线等等。
ESC 的 ASCII 码是 27,有好几种写法:一种是字符表示的 \e
,一种是 16 进制的 \0x1b
(27 对应的 16进制),一种是 8 进制的 \033
,这三种都表示 ESC。
我们来试验一下: 1 表示加粗、36 表示前景色为青色、4 表示下划线,下面三种写法等价:
\e[36;1;4m \033[36;1;4m \0x1b[36;1;4m
我们来试一下:
都打印了正确的样式!
当然,加了样式还要去掉,可以加一个 \e[0m
就可以了(\033[0m
,\0x1b[0m
等价)。
chalk(nodejs 的在终端打印颜色的库)的不同方法就是封装了这些 ASCII 码的颜色控制字符。
上面每行代码被高亮过以后的代码是:
这样也就实现了不同颜色的打印。
至此,我们能实现开头的效果了:支持 code frame 的打印,支持语法高亮,能够打印颜色
本文我们探究了这种效果的实现原理,先从 code frame 是怎么拼接的,然后每一行的代码是怎么做高亮的,之后是高亮具体是怎么打印颜色的。
不管是 code frame 的打印,还是语法高亮或者控制台打印颜色,都是特别常见的功能,希望这篇文章能够帮你彻底掌握这 3 方面的原理。
更多编程相关知识,请访问:编程视频!!
以上是詳解nodejs在控制台列印高亮程式碼的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!