首頁  >  文章  >  web前端  >  詳解nodejs在控制台列印高亮程式碼的方法

詳解nodejs在控制台列印高亮程式碼的方法

青灯夜游
青灯夜游轉載
2021-05-12 10:46:282566瀏覽

這篇文章跟大家介紹一下nodejs如何在控制台列印高亮程式碼。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

詳解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 },
}, {
  highlightCode: 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' ],
  [ '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 的識別和分類。

詳解nodejs在控制台列印高亮程式碼的方法

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

詳解nodejs在控制台列印高亮程式碼的方法

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

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

@babel/highlight 也是這麼做的:

詳解nodejs在控制台列印高亮程式碼的方法

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

如何在控制台打印颜色

控制台打印的是 ASCII 码,并不是所有的编码都对应可见字符,ASCII 码有一部分字符是对应控制字符的,比如 27 是 ESC,就是我们键盘上的 ESC 键,是 escape 的缩写,按下它可以完成一些控制功能,这里我们可以通过打印 ESC 的 ASCII 码来进入控制打印颜色的状态。

格式是这样的:

詳解nodejs在控制台列印高亮程式碼的方法

打印一个 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

我们来试一下:

詳解nodejs在控制台列印高亮程式碼的方法

都打印了正确的样式!

当然,加了样式还要去掉,可以加一个 \e[0m 就可以了(\033[0m,\0x1b[0m 等价)。

chalk(nodejs 的在终端打印颜色的库)的不同方法就是封装了这些 ASCII 码的颜色控制字符。

上面每行代码被高亮过以后的代码是:

1詳解nodejs在控制台列印高亮程式碼的方法

这样也就实现了不同颜色的打印。

总结

至此,我们能实现开头的效果了:支持 code frame 的打印,支持语法高亮,能够打印颜色

1詳解nodejs在控制台列印高亮程式碼的方法

本文我们探究了这种效果的实现原理,先从 code frame 是怎么拼接的,然后每一行的代码是怎么做高亮的,之后是高亮具体是怎么打印颜色的。

不管是 code frame 的打印,还是语法高亮或者控制台打印颜色,都是特别常见的功能,希望这篇文章能够帮你彻底掌握这 3 方面的原理。

更多编程相关知识,请访问:编程视频!!

以上是詳解nodejs在控制台列印高亮程式碼的方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:juejin.cn。如有侵權,請聯絡admin@php.cn刪除