Blogger マークダウン記法で記事を書いて、プログラミングのコードをシンタックスハイライトしたい。

最終更新日:





以前は、はてなブログで書いていたので、同じような感じで書けるようにやったことのまとめです。





Markdown で記事を書きたい。


コマンドラインツール pandoc を使って Markdown から HTML に変換する。 リリースページ からダウンロードできる。

$ pandoc -f markdown index.md > index.html

変換した HTML を「新しい投稿」から貼り付ける。シンプル



目次を表示したい。


VSCode の拡張機能 Markdown All in One を使って目次を作成する。 目次を作成したい場所にカーソルを移動して、コマンドパレットから Markdown All in One: Create table of contents を実行する。 一度コマンドを実行すると自動で変更を検知してくれて素晴らしい。

- [Markdown で記事を書きたい。](#markdown-で記事を書きたい)
  - [目次を表示したい。](#目次を表示したい)
- [コードをシンタックスハイライト付きで表示したい。](#コードをシンタックスハイライト付きで表示したい)
- [テーマの HTML を直接編集したくない。分割してマージしたい。](#テーマの-html-を直接編集したくない分割してマージしたい)



コードをシンタックスハイライト付きで表示したい。


highlight.js を使ってコードをシンタックスハイライトする。

highlightjs.orgに書かれいている通り、CSS と JavaScript を読み込んで hljs.highlightAll(); を実行。リポジトリ の styles 以下にあるファイル名 github-dark-dimmed.min.css を付ければ default 以外のテーマが使える。

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/github-dark-dimmed.min.css" />

<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
<script>hljs.highlightAll();</script>



テーマの HTML を直接編集したくない。分割してマージしたい。

「テーマ」->「カスタマイズ」->「詳細設定」に「CSSを追加」という項目がある。との記事を検索で目にしましたが、そんなものは見つかりません。 これは「直接 HTML を編集しろ」ということなんだろうと思いファイルを分割してマージすることにしました。 でもこれには一つ問題がありまして、テーマの HTML を編集すると「レイアウト」の設定が初期化される。 つまり、変更を加えてコピペするわけですが、その度にレイアウトの設定をし直す必要があります。なんとかならんのか。


追記: と思いましたが、もしかして、ガジェットの追加からトップページと記事のページそれぞれ「HTML/JavaScript」で HTML として JavaScript や CSS を追加しろということなのか?謎。


直接 XML を編集すると JavaScript や CSS は編集がやりづらいので、以下ファイルを分割して go の text/template を使って theme.xml とそれぞれのファイルを結合するコード。

./theme/src
├── theme.xml
├── css
│  └── styles.css
├── js
│  └── index.js
└── xml
   ├── footer.xml
   └── header.xml


theme.xml の中身は省略してますが、以下のようになっていて {{ .XXXX }} の部分が、それぞれのファイルの内容に置き換えられる。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html>
<html>
  <head>
    <b:template-skin>
    <![CDATA[
{{ .CSS }}
    ]]>
    </b:template-skin>
  
{{ .Header }}

  </head>
  <body>

{{ .Footer }}

    <script>
{{ .Script }}
    </script>
 
  </body>
</html>
 


main.go の中身。 html/template ではなく、text/template を使わないと、出力される XML が全てエスケープされる事になるので注意。 標準出力に出力されますが、os.Stdout をファイルの io.Writer にすればファイルに出力できる。

package main

import (
    "fmt"
    "io"
    "os"
    "path/filepath"
    "text/template"
)

func readFile(name string) (string, error) {
    b, err := os.ReadFile(name)
    return string(b), err
}

type Data struct {
    Script string
    CSS string
    Header string
    Footer string
}

func (d *Data) readScriptFile(name string) error {
    script, err := readFile(name)
    if err != nil {
        return err
    }
    d.Script = script
    return nil
}

func (d *Data) readCSSFile(name string) error {
    css, err := readFile(name)
    if err != nil {
        return err
    }
    d.CSS = css
    return nil
}

func (d *Data) readHeaderFile(name string) error {
    text, err := readFile(name)
    if err != nil {
        return err
    }
    d.Header = text
    return nil
}

func (d *Data) readFooterFile(name string) error {
    text, err := readFile(name)
    if err != nil {
        return err
    }
    d.Footer = text
    return nil
}

// それぞれのファイルのパス
type Files struct {
    ThemeXML string
    Script   string
    CSS      string
    Header   string
    Footer   string
}

func newData(files Files) (*Data, error) {
    data := new(Data)
    if err := data.readScriptFile(files.Script); err != nil {
        return nil, err
    }
    if err := data.readCSSFile(files.CSS); err != nil {
        return nil, err
    }
    if err := data.readHeaderFile(files.Header); err != nil {
        return nil, err
    }
    if err := data.readFooterFile(files.Footer); err != nil {
        return nil, err
    }
    return data, nil
}

func readThemeXML(name string) (string, error) {
    return readFile(name)
}

func run(output io.Writer, files Files) error {
    theme, err := readThemeXML(files.ThemeXML)
    if err != nil {
        return err
    }

    data, err := newData(files)
    if err != nil {
        return err
    }

    tpl, err := template.New("").Parse(theme)
    if err != nil {
        return err
    }
    return tpl.Execute(output, data)
}

func main() {
    src := "/home/user/blog/theme/src"
    files := Files{
        ThemeXML: filepath.Join(src, "theme.xml"),
        Script:   filepath.Join(src, "js/index.js"),
        CSS:      filepath.Join(src, "css/styles.css"),
        Header:   filepath.Join(src, "xml/header.xml"),
        Footer:   filepath.Join(src, "xml/footer.xml"),
    }
    output := os.Stdout

    if err := run(output, files); err != nil {
        fmt.Fprintf(os.Stderr, "Error: %v\n", err)
    }
}





コメント