Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

ビルド情報をHTMLレベルで埋め込みたい #30

Open
saki7 opened this issue Nov 1, 2017 · 27 comments
Open

ビルド情報をHTMLレベルで埋め込みたい #30

saki7 opened this issue Nov 1, 2017 · 27 comments

Comments

@saki7
Copy link
Contributor

saki7 commented Nov 1, 2017

背景

Kunaiの実装では、実は最初はレガシーなページの表示をしていて、後から色々と上乗せしています(JavaScriptを丸ごとオフにすると、本来の表示を見ることができます)。

現行の実装

今のkunaiでは、ページを表示した後に、常に最新のmdファイルをGitHubのリポジトリからフェッチしています。

https://github.com/cpprefjp/kunai/blob/69ee7acd3ef1cd6d367f7636f980acad7f0d43c6/js/kunai/meta/meta.js#L59-L77

Kunai::Metaでは、このmdファイルをMarkdownパーサでパースした上で、一部のメタ情報を抜き出してcpprefjpそのもののUX向上に使っています。

蛇足ですが、これはフェッチ自体に 250msec, パース&処理に 6msec ほどで済んでいるので、体感ではあまり感じないと思います。

情報1. ルートのコミットハッシュ

Kunai::Metaを実現するために使っている1つ目のビルド情報が、ルートのコミットハッシュです。

ルートのコミットハッシュは GitHub では Tree: に相当するので、 git clone をすることなく 1つのコミットハッシュで全てのファイルのスナップショットが取れます。


fadasda


今は単純に master をコミットハッシュとして使っているので、常にmasterが取得されます。タイムラグによってはcpprefjpのサイト全体が不定な動作をする可能性があります。

情報2. 現在ページのリポジトリ内相対パス

2つ目のビルド情報が、今見ているページのmdのオリジナルリポジトリルートからの相対パスです。これは今は、

https://github.com/cpprefjp/kunai/blob/69ee7acd3ef1cd6d367f7636f980acad7f0d43c6/js/kunai/meta/meta.js#L253

我ながら寒気がするような実装ですが、信じられないことに右上の「編集」ボタンのリンク先から取得しています。


改善案

実装としては、 base.html のテンプレートを

<head
  data-kunai-build-repo="cpprefjp/site"
  data-kunai-build-hash="3df4629d87cb4af43102b868cc224b7a6377d677"
  data-kunai-build-source="reference/bitset/flip.md"
>
  <!-- ... -->
</head>

このようにすれば、スマートに解決できると思います。

@saki7
Copy link
Contributor Author

saki7 commented Nov 1, 2017

注意としては、data属性に埋め込む時点でHTMLのエスケープを入れる必要があるということです。jinjaに丸投げすれば多分いける

@melpon
Copy link
Member

melpon commented Nov 1, 2017

yabai...>右上の「編集」ボタンのリンク先から取得

data-kunai-build-hash="master" とかやっておけば、もう自動更新すら走らせる必要無くなるかなとか思いましたけど、リンク付けたりシンタックスハイライトしたりとかがあるから、まだ必要そうですね。
Markdown を持ってこなくても、必要なメタ情報があるなら、それを HTML に埋め込んで置くことはできそうです。

@saki7
Copy link
Contributor Author

saki7 commented Nov 1, 2017

@melpon

data-kunai-build-hash="master" とかやっておけば、もう自動更新すら走らせる必要無くなるかなとか思いましたけど、リンク付けたりシンタックスハイライトしたりとかがあるから、まだ必要そうですね。

自動更新は必要です! 静的コンテンツをきちんと生成して初めてインターネットが健全になります。kunaiみたいなのはあくまでオマケです。

言われて思いついたのですが、逆に考えて「GitHubが更新されていたら画面上に更新表示を出す」とかはフロントエンドでできますね。15分ごとにポーリングとかで。それはそれでいいかもしれない。

Markdown を持ってこなくても、必要なメタ情報があるなら、それを HTML に埋め込んで置くことはできそうです。

これを僕がやり始めるとすごく大変そうなのでやってませんでした。これをやって頂けるとかなり理想に近づきます。mdのXHR GETリクエストを消せるので。

今 Kunai::Meta で使っているメタ情報は、

  • サンプルコードのソースコード
  • サンプルコードがページ中のコードブロックのうち何番目のDOM要素に相当するのか
  • cpprefjp/markdown_to_html が meta.py で認識する全てのデータ

などです。漏れがあるかもしれませんが、 meta/meta.js の中で完結していると思います。

mdから取れない情報は基本的に使っていなくて、そういうのは上記のdata-属性に入れたと思います。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

こんな感じの JSON を <header> 要素の data-kunai-mdinfo とかそういう属性に入れておこうと思います。

{
  "meta": {
    "cpp": ["cpp11", "cpp17deprecated"],
    "header": ["thread"],
    "id-type": ["class"],
    "namespace": ["std::this_thread"],
    "class": ["id"]
  },
  "sources": [{
    "id": "...そのコードブロックの要素を指し示すID...",
    "source": "...実行可能なソース..."
  }, {
    "id": "...",
    "source": "..."
  }, {
    ...
  }]
}

@melpon
Copy link
Member

melpon commented Nov 2, 2017

今ってサンプルコードかどうかを判別するのが結構泥臭い方法でしかできないから、これはむしろ Markdown の方に何か情報を入れていっても良いかもしれないですね。

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

こんな感じの JSON を

要素の data-kunai-mdinfo とかそういう属性に入れておこうと思います。

あざす!

今ってサンプルコードかどうかを判別するのが結構泥臭い方法でしかできないから、これはむしろ Markdown の方に何か情報を入れていっても良いかもしれないですね。

そうなんですよね。なるべくsiteに変更を入れない方針なのでこれは悩みどころだったのですが、結局必要な気がしています。

今のサンプルコードの判別方法は

https://github.com/cpprefjp/kunai/blob/617c8af56d30284944b7fd2d81a46ea42680d5e1/js/kunai/meta/meta.js#L155-L164

これなので………

@yumetodo
Copy link
Member

yumetodo commented Nov 2, 2017

markdownのlang指定をc++cppで分けてしまうのが手っ取り早いと思うんですけどねぇ・・・
https://github.com/EzoeRyou/cpp17book
でやってるみたいに

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

@yumetodo むしろCommonMarkでこういう仕様が標準化されているならそれを使いたいくらいなのですが、こういう決まりを自前で作るなら markdown_to_html のスペシャルシンタックス扱いにした方が管理がしやすいのです。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

こっちではこういうコード書いてました。ひどい https://github.com/cpprefjp/bite/blob/d39c0a7a0e26ca5e61906795d80526e03df61e11/main.py#L61-L65

c++cpp だと、サンプルコードだというのが予備知識無いと分からないし、どっちがどっちなのかすぐ忘れそうなので、cpp:example とかにした方が良いかも。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

何かの特殊なシンタックスでもいいですね。

@yumetodo
Copy link
Member

yumetodo commented Nov 2, 2017

cpp:exampleというよりcpp:runnableかと。サンプルコードでもコンパイルが通らないものもあるし、コンパイル・実行可能かを示すべきでは

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

cpprefjp/site#472 (comment)

既にこういう提案が上がっていて、

案2: cpprefjp/markdown_to_html が 認識する新しいタグ を作る (e.g. * function[meta special])

これは 「リファレンス内のページだがメンバ関数ではないベージのとき、本来何を対象にして解説しているページなのか」を表すスペシャルシンタックスの提案です(この例では function = 「非メンバ関数」)。

なので、こういうやり方をするのであれば

* cpp[meta sample-code]

が良いのではないかなと。


@yumetodo:

cpp:exampleというよりcpp:runnableかと。サンプルコードでもコンパイルが通らないものもあるし、コンパイル・実行可能かを示すべきでは

runnable かどうかは定性的に判断できないので、もっとゆるふわな表現の方が僕は好きです。

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

ref: cpprefjp/kunai#18

@yumetodo
Copy link
Member

yumetodo commented Nov 2, 2017

runnable かどうかは定性的に判断できないので

runnableというかwell-formedか。

well-formedか否かは判別できるはず

@faithandbrave
Copy link
Member

すいません、ここでの実行可能とは、Wandboxで実行可能、という理解であっていますか?
たとえば今後ネットワークライブラリが入った場合、(DDoSの踏み台にならないように) Wandboxでは動かないようにするのが正しいかと思いますが 、「インクルードとmain関数を含んだ完全なコード」であることと、「Wandboxで動く」ことでは隔たりがあるのではないかと思います。
これは、コード抽出のためのタグ付けと、フロントエンドのWandbox連携のためのコード抽出では、分けて考えておいたほうがいいのでは、という意図です。
単に「実行可能」であることをタグ付けしちゃうと困りそうだなと。

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

http://spec.commonmark.org/0.28/#fenced-code-blocks

CommonMark における Fenced code block の info string を使うなら、スペースの後にオプションを入れれば良いのですが、

```cpp example

markdown_to_html とか kunai の現行のMarkdownパーサとか site に元からあるシンタックスハイライトがこれに準拠しているかどうかは不明。

僕は心理的には Markdown のコードブロックに cpp example と書くよりは cpprefjp の独自記法の範囲でやる方が良いような気がしています。 @melpon はどう思いますか?

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

@faithandbrave:

すいません、ここでの実行可能とは、Wandboxで実行可能、という理解であっていますか?
たとえば今後ネットワークライブラリが入った場合、(DDoSの踏み台にならないように) Wandboxでは動かないようにするのが正しいかと思いますが 、「インクルードとmain関数を含んだ完全なコード」であることと、「Wandboxで動く」ことでは隔たりがあるのではないかと思います。
これは、コード抽出のためのタグ付けと、フロントエンドのWandbox連携のためのコード抽出では、分けて考えておいたほうがいいのでは、という意図です。
単に「実行可能」であることをタグ付けしちゃうと困りそうだなと。

はい。僕も同じように思います。あくまで、 「このコードはサンプルコードです」 ということが示せればそれで良いのです。僕が余計なことを書きました。

@faithandbrave
Copy link
Member

## 例の下にあるcppコードブロックは、ぼくが把握している限りでは、全てインクルードを含んだ完全なコードになっているはずです。そうなっていなければ、記事側を直したほうがよいと思います。
たとえば翻訳単位とかの説明のために、ヘッダとソースファイルで分かれているようなものは、## 例の下には置いていないつもりです。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

@saki7 メタ情報でやる場合、今のメタ情報はファイル単位でしか使えないので、コードブロック毎に情報を設定できる方法が必要になります。
また、コードブロック毎に設定できる拡張として * std::vector[color ff0000] とかありますが、これはコード中の文字列に対する修飾なので、コード全体に対する修飾を付けるなら、何か別の構文にした方が良いと思います。

で、パッと良い感じの構文は思いつかないので、まあ cpp:hogehoge にしておくのでいいかなーと消極的な感じで cpp:hogehoge に一票
(CommonMark に拘るつもりは無いので cpp hogehoge よりは cpp:hogehoge の方が好みという理由で後者です)

# cpp:wandbox とか cpp:editable とか cpp:tryonline とか思いつきました

@yumetodo
Copy link
Member

yumetodo commented Nov 2, 2017

すいません、ここでの実行可能とは、Wandboxで実行可能、という理解であっていますか?

それもあって

runnableというかwell-formedか。

と発言しました。


構文的にはcpp:hogehogeに一票

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

※僕もアキラさんの意見に賛成で、「例」以下のコードブロックで動かないものがあれば、記事側を直した方が良いという認識です。
※つまるところ、 runnableかどうかはこのIssueの範囲では問題にしていないということです。


@melpon:

@saki7 メタ情報でやる場合、今のメタ情報はファイル単位でしか使えないので、コードブロック毎に情報を設定できる方法が必要になります。

あー。言われてみれば、逆にこっち(→僕がさっき書いた方法)の方が大変ですね。

試してみたのですが、

cpp:example

int a = 42;
std::string s = "これは cpp:example";
<pre lang="cpp:example">

cpp example

int a = 42;
std::string s = "これは cpp example";
<div class="highlight highlight-source-c++">

GitHubだと前者が死んでしまうので、 site がGitHubでプレビューできる以上は、やはりCommonMarkに準拠してスペースを入れるべきな気がします。

その上で、タグを何にするかですが、 @melpon#30 (comment) で書いていたので cpp example で良いと思います。 example なら直感的なので、書く時に思い出しやすいと思います。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

@faithandbrave さんが一番サンプルコードを書いてると思うのですが、

```cpp example
#include <foo>

int main() {
    ...
}
```

って書いてたら、その場で編集して実行できるようになるというので問題ないですか?(既存のを入れ替えるのは、ある程度自動で抽出して適用するのはやれます)

@faithandbrave
Copy link
Member

あまり新ルールを入れると、コントリビューターの参入障壁が上がるのでルールは少ないほうがよい、というのが基本的な考えです。
ですので、cpprefjpに限っては## 例の下にあるcppコードブロックを探せば全てのページで解決できるからパーサーをがんばればよさそう、と思うのですが、これがboostjpを視野に入れたものであればタグ付けは必要かと思います。

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

あまり新ルールを入れると、コントリビューターの参入障壁が上がるのでルールは少ないほうがよい、というのが基本的な考えです。
ですので、cpprefjpに限っては## 例の下にあるcppコードブロックを探せば全てのページで解決できるからパーサーをがんばればよさそう、と思うのですが、これがboostjpを視野に入れたものであればタグ付けは必要かと思います。

僕も site に仕様変更を入れないで済む可能性も含めて色々考えたのですが、仰るように boostjp もできれば綺麗にしたいですし、 cpprefjp の範囲でも article/ 以下の記事は既にサンプルコードの検知で限界が来ています。なので、僕はやはり cpp example は必要という結論に至りました。


@melpon

site 側の書き方の仕様変更はこれで大丈夫です。これに、 #30 (comment) のメタ情報があれば、こちらで対応できます。

これまで上がっているメタ情報の追加の他に、html変換後の コードブロックを <div class="yata"> で丸ごと囲うように修正して頂けますか? (中身は今までと同じ codehilite そのままでOKです)


@faithandbrave

cpp example の書き方で問題がなければ、 cpprefjp の方に周知したいです。独自記法のヘルプなどの変更などお願いできますでしょうか。

@saki7
Copy link
Contributor Author

saki7 commented Nov 2, 2017

※念のため補足ですが、 このIssueの内容が全て実装された場合、 Kunai は md を見に行かなくなるので、 cpp example と書いたらそのまま実行可能になるわけではありません。 @melpon<head> に JSON を入れる実装をした時点で Kunai の挙動を切り替えます。それまでは現行の挙動を保持するつもりです

@faithandbrave
Copy link
Member

ひとまず、ぼくはその方法で合意します。
ですが、このリポジトリはウォッチしている人が少ないので、cpprefjp/site以下でも合意をとってもらえますか?1週間ほど意見を募集して反対意見がなければ決定でよいです。
その上で、編集者向けドキュメントを更新しようと思います。

@melpon
Copy link
Member

melpon commented Nov 2, 2017

これまで上がっているメタ情報の追加の他に、html変換後の コードブロックを <div class="yata"> で丸ごと囲うように修正して頂けますか? (中身は今までと同じ codehilite そのままでOKです)

了解です。

{
  "meta": { ... },
  "sources": [{
    "id": "qawsedrftgyhujikolp",
    "source": "..."
  }]
}

という情報に対して <div id="qawsedrftgyhujikolp" class="yata"> で囲うようにします。

# for free to join this conversation on GitHub. Already have an account? # to comment
Projects
None yet
Development

No branches or pull requests

4 participants