MarkdownとGlamourでGo製ツールの画面出力を色付かせる

最近Charmというプロジェクトのライブラリを使ってみています。Charmでは、CLIにText InputやListのようなリッチなコンポーネントを組み込める Bubble Tea や、ターミナル上の色やレイアウトなどのスタイルを定義する Lip Gloss など、便利なライブラリが公開されています。その中でも、ターミナルでMarkdownを表示するライブラリ Glamour を使って、Go製ツールの画面出力を色付かせてみました。

github.com

Glamourを使ってみる

まずは簡単にGlamourを使ってみます。以下のコードは、Markdown形式の文字列をGlamourで描画するサンプルです。 ちなみに、ここで使っている heredoc というライブラリは、インデントを保ったままヒアドキュメントを書くためのものです。

package main

import (
    "fmt"

    "github.com/MakeNowJust/heredoc"
    "github.com/charmbracelet/glamour"
)

func main() {
    md := heredoc.Doc(`
      # Hello world
      text text text text

      - Item1
      - Item2
        - Item2-1
        - Item2-2
  `)

    out, err := glamour.Render(md, "dark")
    if err != nil {
        panic(err.Error())
    }

    fmt.Println(out)
}

このコードを実行すると、下図のように描画されます。最高ですよね。

動的に作ったMarkdownをGlamourで描画する

次に少し複雑な構造体のデータをもとにMarkdownを組み立て、それをGlamourで描画してみます。

package main

import (
    "fmt"

    "github.com/charmbracelet/glamour"
)

func main() {
    type item struct {
        id    string
        name  string
        count int
    }
    type obj struct {
        text  string
        list  []string
        items []item
    }

    v := obj{
        text: "text text text",
        list: []string{
            "Item1",
            "Item2",
        },
        items: []item{
            {"xxx-000", "alice", 0},
            {"xxx-001", "bob", 1},
        },
    }

    md := "# text\n" + v.text + "\n\n"
    md += "# list\n"
    for _, l := range v.list {
        md += "- " + l + "\n"
    }
    md += "# items\n" + "| id | name | count |\n" + "| --- | --- | --- |\n"
    for _, item := range v.items {
        md += fmt.Sprintf("| %s | %s | %d |\n", item.id, item.name, item.count)
    }

    out, err := glamour.Render(md, "dark")
    if err != nil {
        panic(err.Error())
    }

    fmt.Println(out)
}

このコードを実行すると、下図のように描画されます。最高ですよね。

描画を見ると最高だな~と思ってしまうんですが、Markdownを組み立てる部分がどうにもゴチャゴチャしてしまいますね。 そこでMarkdownを組み立てるためのライブラリを作ってみました。

github.com

このライブラリを使って、上記のコードを書き直してみます。随分すっきりしたのではないでしょうか。

package main

import (
    "fmt"
    "strconv"

    "github.com/charmbracelet/glamour"
    md "github.com/w-haibara/markdown-builder/markdown"
)

func main() {
    type item struct {
        id    string
        name  string
        count int
    }
    type obj struct {
        text  string
        list  []string
        items []item
    }

    v := obj{
        text: "text text text",
        list: []string{
            "Item1",
            "Item2",
        },
        items: []item{
            {"xxx-000", "alice", 0},
            {"xxx-001", "bob", 1},
        },
    }

    m := md.NewMarkdown()
    m.Write(
        md.MustHeading("text", 1),
        v.text,
        md.Br(),
        md.MustHeading("list", 1),
        md.RootList(v.list),
        md.Br(),
    )

    table := make([][]string, len(v.items))
    for i, v := range v.items {
        table[i] = []string{
            v.id,
            v.name,
            strconv.Itoa(v.count),
        }
    }
    m.Write(
        md.MustHeading("items", 1),
        md.Table(
            []string{"id", "name", "count"},
            table,
        ),
        md.Br(),
    )

    out, err := glamour.Render(m.Render(), "dark")
    if err != nil {
        panic(err.Error())
    }

    fmt.Println(out)
}

おわりに

MarkdownとGlamourで Go製ツールの画面出力を簡単に色付かせることができました。CLIツールで一度に多くの情報を表示させたいケースなどで使えるのではないでしょうか。