一款优秀的代码高亮库 - rainbow.js
Post

一款优秀的代码高亮库 - rainbow.js

介绍一个优秀的 js 库。

缘起

代码高亮的 js 库也不少,最知名的莫过于 highlightjs,支持你听过的没听过的各种编程语言,兼容你用过没用过的各种浏览器,有着多姿多彩的配色方案。然而,唯有一点我还是选择放弃了它,因为它不能很方便的自定义高亮语言。

现在我的需求是是这样的,有一个自动化测试结果的页面,里面会显示一个测试案例运行的数据,比如控制台输出 stdout, 我需要高亮控制台输出的一些信息,例如:INFO 级别是默认色,WARN 级别是橙色,ERROR 级别是红色。一句话,我需要自定义日志输出高亮。

1
2
3
4
5
6
...
2017-07-14 11:53:55,668 INFO   : Go to my account page
2017-07-14 11:53:58,071 DEBUG  : Now check: MyAccountPage
2017-07-14 11:53:59,804 WARNING: Page loaded time > 2000 ms
2017-07-14 11:54:01,535 ERROR  : Test failed!!!
...

像这样的需求使用 highlightjs 来实现就显得不够灵活,甚至有点无从下手。

初试 RainbowJS

RainbowJS 项目地址: https://github.com/ccampbell/rainbow

RainbowJS 虽然简单而且支持的编程语言也不多(压缩后大小只有不到 3kb),但是恰恰能满足自定义高亮的需求。入门只需要三步即可:

1. 导入配色文件 - css

官方的 github 仓库里提供了 20 多种配色,常见主题配色都可以找到。

1
<link href="/rainbow/css/theme.css" rel="stylesheet" type="text/css" />

2. 使用 <pre><code> 包住你的代码

code 标签里你可以使用data-language 指定代码语言。

1
2
3
4
5
<pre><code data-language="python">def openFile(path):
    file = open(path, "r")
    content = file.read()
    file.close()
    return content</code></pre>

3. 开始高亮你的代码

如果整个过程是同步的,那么你只需要在页面最后导入 RainbowJS 和你需要的高亮语言库,就可以自动高亮代码块。

1
2
3
<script src="/js/rainbow.js"></script>
<script src="/js/language/generic.js"></script>
<script src="/js/language/python.js"></script>

如果你的代码块是异步生成的,你可以选择提前引入 Rainbow 及相关语言的 js 文件,然后调用Rainbow.color()方法来给代码块着色。

// load rainbow js and language support
...
// call your function to generate dyanmc code blocks
...
// finally, highligh your code blocks
Rainbow.color();
// optionally, you can pass in a callback function in color()
// for example:
Rainbow.color(function() {
    console.log('The new blocks are now highlighted!');
});

高亮自定义语言

从前面的例子可以看到 rainbow 的上手还是很简单的,如果要自定义高亮规则应该怎么办?非常简单,只要调用extend方法即可。比如给TrueFalseNone添加constant的 css 类:

1
2
3
4
5
6
7
8
9
10
Rainbow.extend(
  "python",
  [
    {
      name: "constant",
      pattern: /True|False|None/g,
    },
  ],
  "generic"
);

extend方法可以给指定语言添加高亮规则,规则的名字就是即将添加的 css 类的名字,只要匹配了规则中指定的正则表达式,RainbowJS 就会给匹配的结果添加上对应的 css 类。

接下来我们要给 log 添加高亮规则,为了避免去写新的 css 类,我们可以重用主题配色里已经存在的类,根据主题颜色我们暂定高亮规则如下:

1
2
3
4
5
1. debug - 灰色 - css: comment
2. info  - 白色 - css: support
3. warn  - 橙色 - css: string
4. error - 红色 - css: keyword
5. 时间日期格式 - 灰色 - css: comment

对应的 extend 实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// log.js
Rainbow.extend(
  "log",
  [
    // debug level
    {
      name: "comment",
      pattern: /([^ ]*debug).*/gi,
    },

    // info level
    {
      name: "support.tag",
      pattern: /([^ ]*info).*/gi,
    },

    // warn level
    {
      name: "string",
      pattern: /([^ ]*warn).*/gi,
    },

    // error level
    {
      name: "keyword",
      pattern:
        /([^ ]*error[^(]).*|([^ ]*fatal).*|([^ ]*failure).*|([^ ]*failed).*|([^ ]*exception[^(]).*/gi,
    },

    // time format
    {
      name: "comment",
      pattern: /\b([\d\-:,]+)\b/gi,
    },
  ],
  "python"
);

当 log 行正确匹配后就会被添加上对应规则的 css 类,并且所有高亮规则继承自 python 语言,因为我的 log 是从 python 程序中记录的,当中可能会有一些 python 代码,我希望这些代码也能正常被着色。

最后就是在网页中生成对应的代码块,指定高亮语言为 log:

1
2
3
4
5
6
7
8
<pre><code data-language="log">
...
2017-07-14 11:53:55,668 INFO   : Go to my account page
2017-07-14 11:53:58,071 DEBUG  : Now check: MyAccountPage
2017-07-14 11:53:59,804 WARNING: Page loaded time > 2000 ms
2017-07-14 11:54:01,535 ERROR  : Test failed!!!
...
</code></pre>

效果图如下:

自定义Log高亮

一些改进方法

在页面中加载多个 js 文件的写法挺不方便也不好维护,而且也会影响页面加载的速度。比如上文的例子中如果是直接把<script>片段写到页面里会是这个样子。

1
2
3
4
5
6
7
8
9
10
<body>
  ...
  <pre />
  <code />
  ...
  <script src="/js/rainbow.js"></script>
  <script src="/js/language/generic.js"></script>
  <script src="/js/language/python.js"></script>
  <script src="/js/language/log.js"></script>
</body>

一个改进的建议是就是当页面加载完成后再加载 RainbowJS,然后调用高亮函数,你可以这样写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// page html
<script>$(highlightLog);</script>;

// app.js
function highlightLog() {
  $.getScript("/libs/rainbow/rainbow.min.js", function () {
    $.getScript("/libs/rainbow/language/generic.js", function () {
      $.getScript("/libs/rainbow/language/python.js", function () {
        $.getScript("/libs/rainbow/language/log.js", function () {
          Rainbow.color();
        });
      });
    });
  });
}

虽然有点丑,但是功能没问题。如果你使用的是比较流行的requirejs,那么可以这样使用 RainbowJS:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
// config.js
requirejs.config({
    baseUrl: '/libs',
    shim: {
        rainbow: {
            exports: 'Rainbow'
        },
        rainbow_generic: {
            deps: ['rainbow']
        },
        rainbow_python: {
            deps: ['rainbow_generic']
        },
        rainbow_log: {
            deps: ['rainbow_python']
        }
    },
    paths: {
        rainbow: 'rainbow/rainbow',
        rainbow_generic: 'rainbow/language/generic',
        rainbow_python: 'rainbow/language/python',
        rainbow_log: 'rainbow/language/log'
    }
});

// app.js
function highlightLog() {
    require(['rainbow'], function (Rainbow) {
        window.Rainbow = Rainbow; // must
        require(['rainbow_log'], function () {
            Rainbow.color();
        });
    });
}

// page embeded
<script src="/require.js"></script>
<script>
    require(['/config.js'], function () {
        require(['app'], function (app) {
            app.highlightLog();
        });
    });
</script>

再或者先通过 js 的压缩和打包工具将写好的代码进行优化,亦是目前比较流行和推荐的做法。本文旨在介绍和使用 RainbowJS,不做过多拓展,希望对你有所启发和帮助。

如果需要完整的演示代码,可以查看我得 GitHub 项目:https://github.com/tobyqin/testcube

在 Windows 下搭建 Appium + Android 自动化测试环境

Windows UI自动化测试的XPATH实现 - WPATH