前言

当代码运行报错时,我们会打印错误,错误中有堆栈信息,可以定位到对应的代码位置。但有的时候我们希望能够更直接准确的打印报错位置的代码。比如这样:

这个可以使用 @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

我们先不管高亮,实现这样的格式的打印:

有啥思路没?

其实也比较容易想到,传入了源代码、标记开始和结束的行列号,那么我们就能够计算出显示标记(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控制台打印高亮代码的文章就介绍到这了,更多相关nodejs控制台打印高亮内容请搜索Devmax以前的文章或继续浏览下面的相关文章希望大家以后多多支持Devmax!

nodejs控制台打印高亮代码的实现方法的更多相关文章

  1. ios – 如何将PhoneGap调试控制台与CLI集成?

    我需要在config.xml中添加任何内容吗?

  2. 无法使用xCode 4.4启动控制台应用程序

    我有一个包含两个目标的项目–一个iOS应用程序和一个OSX控制台应用程序.后者是使用XcodeFile->NewTarget并选择“CommandLineTool”创建的.此控制台应用程序用于准备iOS应用程序所需的默认数据库–使用CoreData.这一直很好,直到我升级到MountainLion和xCode4.4.现在,当我尝试运行命令行工具时,我收到“无法启动–权限被拒绝”错误.我试过玩签名证

  3. 使用XCode进行调试时如何生成SIGINT?

    我的控制台应用程序捕获SIGINT,以便它可以正常退出.但是,在调试程序时按XCode中的CTRLC无效.我可以找到进程并使用终端窗口向我的进程发送SIGINT,但是我希望有一个更简单的解决方案,我可以在XCode中完成.解决方法调试器控制台的暂停按钮实际上会向您的应用发送SIGINT.如果您想让调试器将信号传递给您的应用程序,您可以执行以下操作:>按调试器的暂停按钮,等待调试控制台获得焦点>键入

  4. ios – 从应用程序扩展打印到控制台

    我一直在玩iOS8中的新自定义键盘应用程序扩展API,使用Swift作为我的首选语言.然而,我注意到的一件事是,println似乎并没有向控制台输出任何输出,大概是因为这些语句是在应用程序扩展中执行而不是包含应用程序.有没有人找到一种方法从应用程序扩展中将语句打印到控制台?

  5. 为什么Xcode 8(iOS 10)在控制台中打印[LogMessageLogging]

    为什么Xcode8打印[LogMessageLogging]在控制台中,当我调用地图视图时?任何人都可以提出一些建议吗?解决方法PrivacyTheunifiedloggingsystemconsidersdynamicstringsandcomplexdynamicobjectstobeprivate,anddoesnotcollectthemautomatically.Toensuretheprivacyofusers,itisrecommendedthatlogmessagesconsiststri

  6. ios – 如何使用lldb expr(DEBUGGING控制台)在运行时设置UIView的框架

    我正在尝试以下方法我越来越我正在使用XCode6.1,LLDB版本lldb-320.4.152解决方法好的,这是一种有效的方法

  7. Xcode控制台中的“Some”关键字是什么意思?

    打印对象的描述会使lldb在对象的描述前面使用关键字“Some”:这个关键字是什么意思;为什么会这样?解决方法Optional是包含两个案例的枚举,none和some:如您所见,Optional可以具有Some值,具有关联值或None.Optional.None实际上是nil的意思.在这种情况下,调试器告诉您someString是一个Optional(a.k.a.String?),其值为Optional.some.它不是Optional.None,因此它不是零.在Swift3之前,这些案例都是大写的,So

  8. UIKit框架-基础控件Swift版本: 3.UILabel方法/属性详解

    前面我们讲解了UI基础控件的UIButton,现在让我们继续往下讲:1.UILabel的状态内容的显示模式内容的位置字体样式文字排序2.常用属性:以下就是我们在实际开发中最常用到的UILabel的属性:3.实现代码由于前面已经把如何创建工程介绍了,这里就不多做第二次介绍了,下面让我们直接来看代码:实现方法:最终的效果:没有高亮时的状态高亮时的状态:好了,这次我们就讲到这里,下次我们继续~~

  9. UIKit框架-基础控件Swift版本: 4.UIImageView方法/属性详解

    前面我把UIButton和UILabel的常用属性讲完了,现在让我们来看看第三个基础控件:1.UIImageView的状态图片的展示状态2.UIImageView的常用属性3.常用的方法4.代码实现在viewDedload中实现:最终效果图:这里有几个动画属性暂时不讲先,等到后面的综合使用时再一起说,这里涉及到的UIImage知识点,下一篇文章里会有讲解.好了,这次就讲到这里,下次我们继续~

  10. Swift学习笔记五——在Background中启用控制台Console Output模式

    在之前的介绍中可以知道,使用Xcode中的background来学习Swift是非常强大的,可以在右侧的界面中实时预览代码中的所有常量或者变量的值,非常方便。但是现在我们学习的Swift应该作为一种控制台程序出现,就像初学C,OC一样。所有代码的打印结果都可以在Console控制台中显示,以区别之前的所有参数值都显示在一起的情况。但是默认打开Background是没有所谓的控制台的。下面简述打开Background的方法。打开Xcode,选择View-->AssistantEditor-->ShowAss

随机推荐

  1. Error: Cannot find module ‘node:util‘问题解决

    控制台 安装 Vue-Cli 最后一步出现 Error: Cannot find module 'node:util' 问题解决方案1.问题C:\Windows\System32>cnpm install -g @vue/cli@4.0.3internal/modules/cjs/loader.js:638 throw err; &nbs

  2. yarn的安装和使用(全网最详细)

    一、yarn的简介:Yarn是facebook发布的一款取代npm的包管理工具。二、yarn的特点:速度超快。Yarn 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。超级安全。在执行代码之前,Yarn 会通过算法校验每个安装包的完整性。超级可靠。使用详细、简洁的锁文件格式和明确的安装算法,Yarn 能够保证在不同系统上无差异的工作。三、y

  3. 前端环境 本机可切换node多版本 问题源头是node使用的高版本

    前言投降投降 重头再来 重装环境 也就分分钟的事 偏要折腾 这下好了1天了 还没折腾出来问题的源头是node 使用的高版本 方案那就用 本机可切换多版本最终问题是因为nodejs的版本太高,导致的node-sass不兼容问题,我的node是v16.14.0的版本,项目中用了"node-sass": "^4.7.2"版本,无法匹配当前的node版本根据文章的提

  4. nodejs模块学习之connect解析

    这篇文章主要介绍了nodejs模块学习之connect解析,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. nodejs npm package.json中文文档

    这篇文章主要介绍了nodejs npm package.json中文文档,本文档中描述的很多行为都受npm-config(7)的影响,需要的朋友可以参考下

  6. 详解koa2学习中使用 async 、await、promise解决异步的问题

    这篇文章主要介绍了详解koa2学习中使用 async 、await、promise解决异步的问题,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Node.js编写爬虫的基本思路及抓取百度图片的实例分享

    这篇文章主要介绍了Node.js编写爬虫的基本思路及抓取百度图片的实例分享,其中作者提到了需要特别注意GBK转码的转码问题,需要的朋友可以参考下

  8. CentOS 8.2服务器上安装最新版Node.js的方法

    这篇文章主要介绍了CentOS 8.2服务器上安装最新版Node.js的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  9. node.js三个步骤实现一个服务器及Express包使用

    这篇文章主要介绍了node.js三个步骤实现一个服务器及Express包使用,文章通过新建一个文件展开全文内容,具有一定的参考价值,需要的小伙伴可以参考一下

  10. node下使用UglifyJS压缩合并JS文件的方法

    下面小编就为大家分享一篇node下使用UglifyJS压缩合并JS文件的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

返回
顶部