例えば次のようなソースブロックがあった時に……
リンクは次のように書きます。
#+begin_src org
[[https://example.com/][example.com]] へ行ってみよう。
#+end_src
エクスポートすると……
みたいに出力されてしまいます。リンクの書き方を説明したいのに、リンクのブラケットやパスの部分が消えてしまうわけです。
昔からそうだったけ?とモヤモヤしながらもう長いこと経つのですが、org-modeの書き方を例示することが多い私はよくこの問題に引っかかります(最近ではこういうのとかこういうのとか)。
C-x 8 RET zero width space と打って見えない空白を[と[の間に入れて回避するのが常なのですが、そろそろ何とかしたいなぁと思って調べました。そのコードをコピペすると空白までコピーされて機能しませんからね。
結果、次のようなコードで回避できました。
(defvar-local my-org-in-html-fontify-code nil)
(advice-add 'org-html-fontify-code :around 'my-org-html-fontify-code-advice)
(advice-add 'org-html-htmlize-region-for-paste :around 'my-org-html-htmlize-region-for-paste)
(defun my-org-html-fontify-code-advice (old-func &rest rest)
(cl-letf (((default-value 'org-link-descriptive) nil)
((default-value 'my-org-in-html-fontify-code) t))
(apply old-func rest)))
(defun my-org-html-htmlize-region-for-paste (old-fun beg end &rest rest)
(when (and my-org-in-html-fontify-code
(eq major-mode 'org-mode))
(remove-text-properties beg end '(htmlize-link nil)))
(apply old-fun beg end rest))
ソースブロックがどのようにHTMLへ変換されるかは org-html-fontify-code
を見ると良いです。テンポラリバッファを作って言語用のメジャーモードを起動し、ソースブロックの内容を挿入します。確実にfont-lockしたら、テキストプロパティを元にhtml化するわけです。
org-modeにはリンクを文字通りに表示する org-link-descriptive
というオプションがあるので、この関数が呼ばれている間だけ nil になってもらうのが先のコードです。
org-link-descriptive
はorg-modeが立ち上がるとバッファローカル変数になってしまいます。なのでエクスポート元のバッファでいくら let で nil にしても、新しく作られるテンポラリバッファでは nil になりません。そこでcl-letfでデフォルト値の方を nil にしています。
何かエクスポート中であることを判別する方法があるならorg-mode-hookでorg-toggle-link-displayでも呼んでしまおうかと思ったのですが、残念ながら見つかりませんでした。
まずはその辺りを修正してからエクスポートしたのがこちら。
リンクがクリックできるようになっているのがちょっと気持ち悪いです。この例では正しいURLになっていますが、URLとは解釈できないようなorg-mode独自のリンクパスも問答無用でHTMLのリンクにしてくれます。
これはhtmlize-linkというテキストプロパティの作用です。ソースのHTML化はhtmlizeというパッケージがバッファ内のテキストプロパティを元に行いますが、org-modeのfont-lockがhtmlize-linkというテキストプロパティを付けるので、それを見たhtmlizeが自動的にリンクに置き換えてしまうのです。
ソースブロックの中からリンクを張りたいなどとは思わないので、こちらも問答無用でhtmlize-linkというテキストプロパティを削除するようにしました。
最終結果は次の通りです。
リンクは次のように書きます。
[[https://example.com/][example.com]] へ行ってみよう。