Code hay dở chưa biết, nhìn phải đẹp đã.

Hugo là một bộ sinh trang web tĩnh (SSG - Static Site Generator) mã nguồn mở. Được xây dựng trên ngôn ngữ Go, Hugo nổi tiếng bởi tốc độ build siêu nhanh (< 1ms mỗi trang) và các tính năng hỗ trợ xây dựng nội dung mạnh mẽ. Từ blog cá nhân, trang quảng bá, trang tài liệu đến trang thương mại điện tử, Hugo đáp ứng được đa dạng nhu cầu về nội dung và lĩnh vực. Bài viết sau đây dành cho người mới bắt đầu sử dụng Hugo.

Các cách tiếp cận

Có 2 cách tiếp cận khi nói đến highlight code.

  1. Thực hiện ở phía browser, sử dụng Javascript (Highlight.js, Prism.js).
  2. Thực hiện ở phía server, sinh sẵn mã HTML highlight bằng ngôn ngữ phía server.

Từ browser

Nhiều theme Hugo hỗ trợ cách tiếp cận đầu tiên, chẳng hạn, theme Paper cho phép cấu hình sử dụng Highlight.js.

Từ server

Cách tiếp cận số 2 trong Hugo sử dụng Chroma để highlight code. Chroma, viết bằng Go, thực hiện chuyển mã nguồn thành mã HTML có highlight cú pháp với hiệu năng cao. Chroma hỗ trợ nhiều ngôn ngữnhiều theme phổ biến. Thử nghiệm highlight code bằng Chroma tại đây.

Ưu điểm của cách tiếp cận này so với cách thứ nhất:

  • Sử dụng được với mọi theme, do được hỗ trợ mặc định bởi Hugo
  • Hiệu quả hơn, do mã HTML highlight được sinh sẵn ở build time, cũng như không cần tải thêm Javascript ở browser

Phần còn lại của bài viết tập trung hướng dẫn highlight code theo cách tiếp cận số 2.

Cấu hình mặc định

Ta có thể đặt cấu hình mặc định cho hightlight code trong file config.

# config.yml
markup:
  highlight:
    anchorLineNos: false  # Nếu true, sinh ra link cho từng dòng code
    guessSyntax: false
    hl_Lines: ""
    hl_inline: false      # Nếu true, highlight thẻ `code` markdown
    lineAnchors: ""
    codeFences: true  # Nếu true, cho phép truyền tham số vào code fence trong Markdown
    lineNos: false    # Nếu true, hiện số thứ tự dòng code
    lineNoStart: 1    # Số thứ tự bắt đầu của dòng code
    lineNumbersInTable: true
    noClasses: true   # Nếu false, sử dụng highlight bằng Javascript
    noHl: false
    style: monokai    # Theme
    tabWidth: 4       # Độ rộng tab

Sử dụng shortcode

Hugo hỗ trợ sẵn shortcode highlight để làm nhiệm vụ như tên gọi của nó.

{{< highlight go "linenos=true,hl_lines=11 20-26,linenostart=2" >}}
// ... code
{{< / highlight >}}
 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
package main

import (
    "fmt"
    "math/rand"
    "time"
)

type Moo struct {
    Cow   int
    Sound string
    Tube  chan bool
}

// A cow will moo until it is being fed
func cow(num int, mootube chan Moo) {
    tube := make(chan bool)
    for {
        select {
        case mootube <- Moo{num, "moo", tube}:
            fmt.Println("Cow number", num, "mooed through the mootube")
            <-tube
            fmt.Println("Cow number", num, "is being fed and stops mooing")
            mootube <- Moo{num, "mooh", nil}
            fmt.Println("Cow number", num, "moos one last time out of happyness")
            return
        default:
            fmt.Println("Cow number", num, "mooed through the mootube and was ignored")
            time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
        }
    }
}

Sử dụng code fence

Khi cấu hình codeFences: true trong file config, Hugo cho phép truyền tham số vào cú pháp code fence của Markdown.

```go {linenos=table,hl_lines=[11,"15-17"],linenostart=2}
// ... code
```
 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
package main

import (
    "fmt"
    "math/rand"
    "time"
)

type Moo struct {
    Cow   int
    Sound string
    Tube  chan bool
}

// A cow will moo until it is being fed
func cow(num int, mootube chan Moo) {
    tube := make(chan bool)
    for {
        select {
        case mootube <- Moo{num, "moo", tube}:
            fmt.Println("Cow number", num, "mooed through the mootube")
            <-tube
            fmt.Println("Cow number", num, "is being fed and stops mooing")
            mootube <- Moo{num, "mooh", nil}
            fmt.Println("Cow number", num, "moos one last time out of happyness")
            return
        default:
            fmt.Println("Cow number", num, "mooed through the mootube and was ignored")
            time.Sleep(time.Duration(rand.Int31n(1000)) * time.Millisecond)
        }
    }
}

Tham khảo: