Typstでコードを埋め込むには【codelstパッケージ】

# はじめに
[Typst](https://typst.app/)はここ最近広がりを見せている新しい組版システムです。学術論文の作成に利用可能であり、一般にLaTeXと比べて高速なコンパイルを行えることが特徴です。

この記事では、Typstでソースコードをレンダリングするためのパッケージである、[codelst](https://typst.app/universe/package/codelst)についての簡単な説明と使い方を紹介します。

# ソースコードのレンダリング
## 基本的な使い方
codelstパッケージは次のように記述することで導入が可能です。今回はバージョン2.0.0を利用します。
 
~~~import_codelst.typ
#import "@preview/codelst:2.0.0": sourcecode
~~~

これによって、codelstパッケージが導入され、`sourcecode`関数が利用可能になります。この関数を利用して、ソースコードを次のように記述することができます。

:::note warn
追記 2024/04/23
codelst v2.0.1が公開されました。これ以前のバージョンを使っているとコードの行番号の表示がうまくいかないようなので、最新バージョンを使うようにしておくと良いでしょう。
:::



~~~
#import "@preview/codelst:2.0.0": sourcecode

#sourcecode[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~
このように記述することで、次のようにソースコードがレンダリングされます。

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/b9100604-01fe-6eb7-e7f4-fa52a3cab2c9.png)


行番号とともにソースコードが表示されていることがわかります。余談ですが、Typst標準のコードブロックも存在し、次のような記述で利用することができます。

~~~
```c
#include <stdio.h>
int main() {
    printf("Hello, World!\n");
    return 0;
}
```
~~~

このように記述することで、次のようにソースコードがレンダリングされます。

![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/5a599585-7598-95f3-34b0-160aa6c77261.png)


このように、表示したいソースコードをトリプルバッククォート(```` ``` ````)で囲むことで、Typst標準のコードブロックを利用することができます[^1]。トリプルバッククォートの直後に拡張子を指定することで、適切なシンタックスハイライトも行われます(今回はC言語のサンプルコードを用いています)。しかし、学術論文やレポートを書く際には、行番号の表示や装飾を行いたいことが多いため、codelstパッケージを利用するのがよさそうです。

[^1]: これはMarkdownの記法と同じなので、慣れ親しんでいる方が多いかもしれません。

## ファイルの読み込み
codelstパッケージを利用する際、ソースコードをそのまま貼り付けるのではなく、ファイルを読み込んでソースコードを表示することも可能です。その場合、`sourcefile`関数を利用して次のように記述します。

~~~
#import "@preview/codelst:2.0.0": sourcefile, 

#sourcefile(read("Hello_world.c"), file:"Hello_world.c")
~~~

このように記述することで、`Hello_world.c`というファイルを読み込んで、その内容を表示することができます。

![003.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/dbe308d0-bd99-a0e5-bc6d-558748aaf1dd.png)


`sourcecode`関数と同様に表示が行えていることがわかると思います。

## フレームのカスタム
ここまで省略していましたが、`sourcecode`関数を利用する際は基本的に`#sourcecode(<オプション>)[<ソースコード>]`という形式で記述します。ここではオプションを指定することによるフレームのカスタムについて紹介します。

### フレームの非表示
オプションにおいて`frame: none`を指定することで、フレームを非表示にすることができます。

~~~
#sourcecode(
    frame: none
)[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~

![004.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/c03bb369-6689-3934-44da-352e11e3fe11.png)

### フレームの枠線
フレームの色や形状を変更するには、`block`関数を利用して、
~~~
#sourcecode(
    frame: block.with(<オプション>)
)[<ソースコード>]
~~~
のように記述すれば良いようです。例えばフレームの枠線を変更する際は次のように記述します。

~~~
#sourcecode(
    frame: block.with(
        stroke: 1pt + fuchsia
    )
)[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~

![005.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/65ec414a-16e6-7742-f448-5868fe93d372.png)

詳しくは公式ドキュメントの[rect要素](https://typst.app/docs/reference/visualize/rect)に関するページのstrokeの欄を参照するのがオススメですが、簡単に説明すると、`stroke`オプションは`<線の太さ> + <色>`という形式で指定することができます。線の太さの指定方法は[length](https://typst.app/docs/reference/layout/length/)、色の指定方法は[color](https://typst.app/docs/reference/color)を参照してください。

### インセット
フレームの内側に余白を設けたいときは、`inset`オプションを指定します。

~~~
#sourcecode(
    frame: block.with(
        stroke: 3pt + fuchsia,
        inset: (x: 10pt, y: 10pt)
    )
)[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~

![006.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/0a19b9d1-604a-b30d-a7d1-67ff181d178a.png)

`inset`オプションはこのように、`x`と`y`の値を指定することで、それぞれの方向に余白を設けることができます。デフォルトは`5pt`です。

### フレームの角丸
フレームの角を丸めるには、`radius`オプションを指定します。

~~~
#sourcecode(
    frame: block.with(
        stroke: 3pt + fuchsia,
        inset: (x: 10pt, y: 5pt),
        radius: 5pt
    )
)[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~

![007.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/9f31776c-9ddb-06b1-9ac1-d93cd2347864.png)

### フレーム内部の背景色
フレーム内部の背景色を変更するには、`fill`オプションを指定します。

~~~
#sourcecode(
    frame: block.with(
        stroke: 3pt + fuchsia,
        inset: (x: 10pt, y: 5pt),
        radius: 5pt,
        fill: fuchsia.lighten(96%)
    )
)[```c
    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }
```]
~~~

![008.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/6fd26e32-20bf-b852-4b36-8e2bdb562e2d.png)

例に示した通り、`lighten`関数を利用することで、指定した色を明るくすることができます。その他、グラデーションなども設定できるようなので、公式ドキュメントの[color](https://typst.app/docs/reference/visualize/color)を参照してみると良いかもしれません。

## ソースコードの表示
### 行番号のカスタム
行番号はデフォルトではソースコードの左側に表示されます。これを右側に表示するには、`numbers-side: right`を指定します。

~~~
#sourcecode(
    numbers-side: right
)[```c
#include <stdio.h>

int main()
{
    int num1, num2, sum, product;

    printf("Enter the first integer: ");
    scanf("%d", &num1);

    printf("Enter the next integer: ");
    scanf("%d", &num2);

    sum = num1 + num2;
    product = num1 * num2;

    printf("The sum of the entered integers is: %d\n", sum);
    printf("The product of the entered integers is: %d\n", product);

    return 0;
}

```]
~~~

![011.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/99d2ac0d-83ee-6d50-20d3-023fde42d4e0.png)

ソースコードの最初の行のコード番号を1以外に変更したい場合は、`numbers-start`オプションを利用します。

~~~
#sourcecode(
    numbers-side: right,
    numbers-start: 10
)
~~~

![012.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/52f67d68-ee04-0872-9a80-afb12d41626a.png)

今回は`numbers-start: 10`を指定しているため、ソースコードの最初の行のコード番号が10になっています。

行番号の表示を開始する行を指定するには、`numbers-first`オプションを利用します。

~~~
#sourcecode(
    numbers-side: right,
    numbers-start: 10,
    numbers-first: 12
)
~~~

![013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/dfc4b345-2c53-350c-3bc2-d3710d0ab4f2.png)

今回は`numbers-first: 12`を指定しているため、ソースコードの行番号が12から表示されています。`numbers-start`と紛らわしいですが、`numbers-start`は行番号の最初の値自体を変更するもので、`numbers-first`は表示を開始する行を指定するものです。

`numbers-step`オプションは行番号の表示間隔を指定するオプションです。デフォルトでは1ですが、例えば2を指定すると、偶数行のみに行番号が表示されるようになります。

~~~
#sourcecode(
    numbers-side: right,
    numbers-start: 10,
    numbers-first: 12,
    numbers-step: 2
)
~~~

![014.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/9df3315c-3078-df51-8e53-899dc1f4de6d.png)

`numbering`オプションを使用すると、ナンバリングに用いる数字の形式を変更することができます。デフォルトでは単なるアラビア数字ですが、例えば`numbering: (1)`のように指定することで、括弧で囲まれたアラビア数字が表示されるようになります。

~~~
#sourcecode(
    numbers-side: right,
    numbers-start: 10,
    numbers-first: 12,
    numbers-step: 2,
    numbering: "(1)"
)
~~~

![015.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/2589ca77-4805-cf16-bf6e-73a5a831a8a7.png)

また、使う場面は少ないと思いますが、`numbering:"I"`のように指定することで、ローマ数字が表示されるようになります。パッと見では行番号が分かりづらそうですが...。

![016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/f9128f4e-cb2f-cecc-0046-2a06eb73a291.png)

また、`numbering:none`を指定することで、行番号を非表示にすることもできます。

### 特定の行をハイライト
特定の行をハイライトするには、`highlighted`オプションを利用します。


~~~
#sourcecode(
    highlighted:(1,3,5)
)
~~~

![017.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/567b7737-1e5d-4995-3953-7732ddda2348.png)

今回は`highlighted:(1,3,5)`を指定しているため、1行目、3行目、5行目がハイライトされています。要するにarrayを渡してやればよいので、`range`関数を利用することで、範囲やハイライトする行の間隔を指定することもできます。

~~~
#sourcecode(
    highlighted:range(1, 10, step:2)
)
~~~

![018.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/ec57e12a-fc96-33f5-ca88-554787e640bc.png)

`range`関数やarrayについては公式ドキュメントの[array](https://typst.app/docs/reference/foundations/array/)を参照してください。

### 空白行について
余談ですが、`sourcecode`関数を利用する際、ソースコード前後の空白行は、デフォルトだと次のように削除されるようです。

~~~
#sourcecode(
    frame: block.with(
        stroke: 1pt + fuchsia,
        inset: (x: 10pt, y: 5pt),
        radius: 5pt,
        fill: fuchsia.lighten(96%)
    )
)[```c

    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }

```]
~~~

![009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/a3c36344-93c4-1df7-96bf-0f382da169ea.png)

もし空白行を削除されたくない場合は、`showlines: true`と記述することにより、空白行をそのまま表示することができます。

~~~
#sourcecode(
    showlines: true,
    frame: block.with(
        stroke: 1pt + fuchsia,
        inset: (x: 10pt, y: 5pt),
        radius: 5pt,
        fill: fuchsia.lighten(96%)
    )
)[```c

    #include <stdio.h>

    int main() {
        printf("Hello, World!\n");
        return 0;
    }

```]
~~~

![010.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3748073/8b3bc4a5-c9ba-39a0-af6a-a9c2956448bd.png)

# おわりに
備忘録代わりの記事ではありますが、間違いなどがあれば指摘していただけると幸いです。また、codelstパッケージには他にも様々なオプションが用意されているため、ぜひ公式ドキュメントを参照してみてください。

# 参考文献
- [Typst](https://typst.app/)
- [codelst – Typst Universe](https://typst.app/universe/package/codelst)
- [typst-codelst/manual.pdf](https://github.com/jneug/typst-codelst/blob/main/manual.pdf)
- [Typst 便利パッケージと使用例の紹介](https://qiita.com/key_271/items/8629980c4c1ff0e55f41)