チュートリアル:Hugo と Compiler Explorer を使用したインタラクティブなコード スニペット

私は現在、私の C++ パーサー コンビネーター ライブラリである lexy のドキュメントを書き直しています。導入部でこれについて言及するのは、これで 4 回連続のブログ投稿です!これには、文法を入力して入力して表示できるインタラクティブなオンライン プレイグラウンドが既にあります。結果の解析ツリーおよび/またはエラー メッセージ。これは非常に役立つため、新しいドキュメントにはプレイグラウンドで直接利用できる例が含まれており、試してみて何が起こるかを確認できます。

これを実装しているときに、ブログの通常のコード スニペットにも拡張できることに気付きました。Javascript や手動の作業を一切行わなくても、コード スニペットに、コンパイラ エクスプローラーに直接リンクする「再生ボタン」を付けることができます。このように:

int main()
{
    fmt::print("Hello, {}!", "World");
}

どうやってそれをやったか見てみましょう。

コンパイラ エクスプローラの API

lexy の Playground と同様に、これも Compiler Explorer の API によって強化されています。これにより、POST 要求を送信するだけで C++ コードをコンパイル (および実行!) できるため、Playground が機能します。Compiler Explorer は驚くべきものです。

ここでは、clientstate GET 要求にさらに関心があります。これにより、00 を指定することで、ソース オプションとコンパイラ オプションが入力された状態で Compiler Explorer を開くだけの URL を作成できます。 base64 でエンコードされた JSON としてのオブジェクト。

たとえば、次の 16 この乱雑な URL になります。

{
  "sessions": [
    {
      "id": 1,
      "language": "c++",
      "source": "int main() {}",
      "compilers": [],
      "executors": [
        {
          "compiler": {
            "id": "clang_trunk",
            "libs": [],
            "options": "-std=c++20"
          }
        }
      ]
    }
  ]
}

これにより、ソース 20 を持つ単一の C++ 入力で CompilerExplorer が開きます ,分解ビューなし (37 )、および外部ライブラリなしのコンパイラとして C++20 モードで clang トランクを使用する 1 つの実行ビュー。

参考までに、<​​code>48 オブジェクトはここで定義されていますが、多くの場合、目的の結果を作成し、ショートリンクを生成し、53 を使用してショートリンクに関する情報をコンパイラ エクスプローラーに問い合わせる方が簡単です。 :https://godbolt.org/api/shortlinkinfo/aEvxefPsT.

Hugo ショートコード

このブログは、静的サイト ジェネレーターである Hugo を使用して構築されています。多くの優れた機能の 1 つにショートコードがあります。ショートコードは、コンテンツ (この投稿のマークダウン ソースなど) に挿入して、任意の HTML に展開できるスニペットです。

ここでは、C++ コードを入力として取り、強調表示された C++ コードに展開するショートコードが必要ですが、コンパイラ エクスプローラーへのリンクも追加します。強調表示された C++ コードのみを実行するのは簡単です。60 を追加します。 次の内容で:

{{ $source := .Inner }}

{{ highlight $source "cpp" "" }}

これは単純に 70 を使用します 関数はすべてを取得し、構文が強調表示された HTML になります。次のように使用できます:

{{< godbolt >}}
#include <fmt/format.h>

int main()
{
    fmt::print("Hello, {}!", "World");
}
{{< /godbolt >}}

結果は次のとおりです。

#include <fmt/format.h>

int main()
{
    fmt::print("Hello, {}!", "World");
}

もちろん、これはすでに Markdown に組み込まれているものなので、拡張して再生ボタンを追加しましょう。そのためには、まず JSON を構築し、それを base64 エンコードして新しいリンクに追加する必要があります。 JSON、89 を使用できます キー値オブジェクトを構築する関数と 92 結果は文字列に変換でき、あとは簡単です:

{{ $source := .Inner }}

{{ $compiler    := dict "id" "clang_trunk" "libs" (slice) "options" "-std=c++20" }}
{{ $executor    := dict "compiler" $compiler }}
{{ $session     := dict "id" 1 "language" "c++" "source" $source "compilers" (slice) "executors" (slice $executor) }}
{{ $clientstate := dict "sessions" (slice $session) }}

{{ $clientstate_b64 := replace ($clientstate | jsonify | base64Encode) "/" "%2F" }}

<a href="https://godbolt.org/clientstate/{{ $clientstate_b64 }}">Try it online.</a>
{{ highlight $source "cpp" "" }}

これにより、以前と同じオプションを使用してコンパイラ エクスプローラ セッションにリンクするコード ブロックへのリンクが追加され、代わりに入力からのソース コードが使用されます。

素敵に仕上げる

これで基本的には完了です。残りは砂糖です。

まず、完全にコンパイル可能な例には、ブログ内に表示したくない不要なボイラープレートが含まれている可能性があります。そのため、正規表現を使用して 102 内のすべてを抽出します。 と 116 コンパイラ エクスプローラーのソースでマーカーを削除しながら、そのインラインを表示します。

{{ $source        := .Inner }}
{{ $full_source   := trim ($source | replaceRE "//({|})\\n" "") "\n" }}
{{ $inline_source := trim ($source | replaceRE "(?s:.*//{(.*)//}.*)" "$1") "\n" }}

…

{{ $session := dict … "source" $full_source … }}

…

{{ highlight $inline_source "cpp" "" }}

テキストの代わりに、実際の Font Awesome 再生ボタンを使用することもできます。

<div class="example-play-button">
  <a href="https://godbolt.org/clientstate/{{ $clientstate_b64 }}">
    <i class="fas fa-play"></i>
  </a>
</div>
{{ highlight $inline_source "cpp" "" }}

私にとっては、見栄えを良くするために次の SCSS が必要です:

// play button for interactive examples
.example-play-button {
    position: relative;
    a {
        position: absolute;
        right: 5px;
    }
}

Compiler Explorer はさまざまな外部ライブラリをサポートしています。おそらく fmt と lexy を使いたいので、それらを有効にしましょう:

{{ $lexy        := dict "id" "lexy" "version" "trunk" }}
{{ $fmt         := dict "id" "fmt" "version" "trunk" }}
{{ $compiler    := dict "id" "clang_trunk" "libs" (slice $lexy $fmt) "options" "-std=c++20" }}

最後に、 120 を使用すると マークダウン コード ブロックの代わりに、C++ コードの編集中に C++ 構文の強調表示、コード補完、およびその他の言語サーバーの機能が失われます。ハックとして、実際にマークダウン コード ブロックをゴッドボルト ショートコード内に配置し、それを取り除きました:

{{ $source := .Inner | replaceRE "(?ms:^```cpp\n(.*)```$)" "$1" }}

結論

これが役に立つことを願っています.今後の投稿で間違いなく使用を開始し、以前の投稿のいくつかも更新する可能性があります.コードをコンパイラ エクスプローラーに貼り付けたり、短縮リンクを取得したり、ショートリンクを同期させておくなど

基本的なシステムは自由に拡張できます。ショートコードは追加のパラメーターを受け取ることができるため、コンパイラーを切り替えたり、135 を介して逆アセンブリを表示するオプションを受け入れることができます。 lexy のドキュメントでは、実際にはソース コードをドキュメントに埋め込んでいません。代わりに 146 を使用しています。 、ここで 155 は、ショートコードによって取り込まれる Hugo アセットです。サンプルは外部ファイルにあるため、テスト スイートに追加して、常にコンパイルされるようにすることができます。

付録:完全なコード

168 ショートコードの定義:

{{ $source        := .Inner | replaceRE "(?ms:^```cpp\n(.*)```$)" "$1" }}
{{ $full_source   := trim ($source | replaceRE "//({|})\\n" "") "\n" }}
{{ $inline_source := trim ($source | replaceRE "(?s:.*//{(.*)//}.*)" "$1") "\n" }}

{{ $lexy        := dict "id" "lexy" "version" "trunk" }}
{{ $fmt         := dict "id" "fmt" "version" "trunk" }}
{{ $compiler    := dict "id" "clang_trunk" "libs" (slice $lexy $fmt) "options" "-std=c++20" }}
{{ $executor    := dict "compiler" $compiler }}
{{ $session     := dict "id" 1 "language" "c++" "source" $full_source "compilers" (slice) "executors" (slice $executor) }}
{{ $clientstate := dict "sessions" (slice $session) }}

{{ $clientstate_b64 := replace ($clientstate | jsonify | base64Encode) "/" "%2F" }}

<div class="example-play-button">
<a href="https://godbolt.org/clientstate/{{ $clientstate_b64 }}"><i class="fas fa-play"></i></a>
</div>
{{ highlight $inline_source "cpp" "" }}

ブログ投稿での使用例:

{{< godbolt >}}
#include <fmt/format.h>

//{
int main()
{
    fmt::print("Hello, {}!", "World");
}
//}
{{< /godbolt >}}

レンダリング:

int main()
{
    fmt::print("Hello, {}!", "World");
}