Hugo 代码高亮
2021-11-07

搭建博客之前从没想过,在文章中放入带语法着色的代码片段,是挺不容易的一件事。

因为实现起来复杂,刚开始一般都没有代码着色功能。这也就给了前端插件生存的空间,例如 Prismhighlight.js。

Prism 是把 HTML 中的 <code> 块里的内容替换成带有语法着色的新内容,就如同 less.js 在浏览器端把 .less 编译成 .css 文件那样。注意,Prism 看到的并不是 markdown 源文件,而是转换之后的 HTML,因为 Prism 是当网页在用户浏览器上渲染时才起作用,而不是在 markdown 转 HTML 的时候。

Prism 的使用体验很不错,只要在网页头部插入 prism.jsprism.css 剩下的它会自动搞定。直到有一天我发现代码的样式不对了,原来是 Hugo 自己的代码着色与 Prism 的掺和到一起了。既然 Hugo 有自带的,就研究了一下,发现完全够用。

与 Prism 的丰富相比,Hugo 显得有些简单,同时也意味着更容易上手,少一些折腾,少一些选择困难。使用 Hugo 的代码块功能,只要简单弄一下配置文件,然后在文章中使用就可以了。下面说说具体做法。

配置文件

打开项目根目录下的 config.toml,加上这么一段

[markup.highlight]
  style = 'manni' # 样式参考 https://swapoff.org/chroma/playground

就是这么简单!网上那些教程写的配置项太多太乱,我逐个试过后,把默认值就很完美的全都删掉(例如 tabWidth = 4)只留下必要且有意义的,发现就只需要选一个自己喜欢的样式,其他啥都不用。目前可选的样式并没有太满意的,manni 也是妥协之后的选择,期待以后会有更好看的。

对于高级玩家,想在 markdown 中插入 HTML 代码的,需要打开一个默认被关闭的选项,就再添加下面这段:

[markup.goldmark.renderer]
  unsafe = true # 默认值不支持在 markdown 中写 html,需改成 true 开启

上面这个配置项写的是 unsafe 看着挺吓人,但其实不必担心,因为用于个人网站的话,所有东西都是自己写的,不会有恶意代码。

markdown 里写代码块

这部分用直观的对照一个一个例子来呈现,先展示代码块渲染在页面上的样子,紧接着展示 markdown 源码怎么写。

1. 最普通的用法

渲染效果:

fn main() {
    println!("Hello, world!");
}

markdown 写法:

```rust
fn main() {
    println!("Hello, world!");
}
```

对于不带行号的代码块,默认的样式布局可能会显得有些局促,调整一下文章的 CSS:

/* 不带行号的代码块 */
.highlight>pre {
  padding: 10px;  /* 适当留出边距 */
  overflow: auto; /* 代码行太长向右滚动 */
}

2. 带行号的

渲染效果:

1
2
3
fn main() {
    println!("Hello, world!");
}

对应的 markdown 写法:

```rust {linenos=table}
fn main() {
    println!("Hello, world!");
}
```

上面代码中用到了扩展语法,也就是紧跟在 rust 后、花括号内的部分。

这里用到的参数是 lineos,有以下两种取值:

.highlight{
  /* 统一行高,让行号和代码能对齐 */
  line-height: 18px;
}

3. 自定义起始行号

渲染效果:

21
22
23
fn main() {
    println!("Hello, world!");
}

对应的 markdown 写法:

```rust {linenos=table, linenostart=21}
fn main() {
    println!("Hello, world!");
}
```

linenostart=21 表示代码块从第 21 行开始计数。典型的使用场景是,在一大段代码中单摘出一小段,为保留原有的代码行号,就可以这么来用。

4. 行高亮

我们有时候会有把某些行高亮,用来强调突出,像下面这样:

渲染效果:

1
2
3
4
5
6
7
fn main() {
    another_function(5);
}

fn another_function(x: i32) {
    println!("The value of x is: {}", x);
}

对应的 markdown 写法:

```rust {linenos=table, hl_lines=[2 "5-6"]}
fn main() {
    another_function(5);
}

fn another_function(x: i32) {
    println!("The value of x is: {}", x);
}
```

这里 hl_lines 就表示有哪些行高亮,它的值是一个集合,上面的例子中有两部分做了高亮。首先是第 2 行,接下来是第 5-6 行,当连续多行高亮时,可以用 ”起始行号-截止行号“ 的写法。其中「起始行号」和「截止行号」并非指左侧的代码行号,而是从这个代码块中的第一行开始数起。例如下面这段:

渲染效果:

21
22
23
fn main() {
    another_function(5);
}

对应的 markdown 写法:

```rust {linenos=table, linenostart=21, hl_lines=[2]}
fn main() {
    another_function(5);
}
```

代码行号从第 21 行开始,高亮的是本代码块的第 2 行,于是实际是第 22 行高亮。

END