コードリーティングの記録とOrg Capture

人間忘れるものなので、コードリーディングする時はorg-modeでメモをとっている。 このメモを取る時なのだけど、単純に文章だけだと後から追いづらい。 なので、気になった箇所は文章と共にファイルリンク1とGitHubへのリンクもセットで記録している。

Code Reading

コードリーディングについては “Code Reading―オープンソースから学ぶソフトウェア開発技法” がおすすめ。 今でこそ慣れたが、はじめはOpenStackとかKubernetesとか大規模なソースコードを読むのに苦戦した。 感覚で改善してしまったが、この本ではその普段何となくやっていたことがうまく言語化されている点が良かった。

org-captureで楽をする

メモと共にリンクを記録しているといったが、流石にファイルパス・行番号・コミットハッシュ・GitHubリンクを手動で調べて挿入するのでは面倒すぎる。 そこで、org-captureを使ってカーソル位置の情報をメモ帳の指定した位置に挿入している。

Video 1: org-captureでコードのリンクをメモに挿入する例

実際の設定はこんな感じ。org-captureの (function) オプションを利用して書き込み先のバッファとカーソル位置を選択、 書き込み先のサブツリーは事前に (org-code-capture--store-here) でマークしておく。

ファイルリンクをコメントアウトしているのは、外部に公開するときはローカルのファイルパスを公開しても意味がないため。 ローカルではコメントアウトされたリンクでもジャンプできるので問題なし。

(defconst ladicle/org-journal-dir "~/Dropbox/org/journal/")
(defconst ladicle/org-journal-file-format (concat ladicle/org-journal-dir "%Y%m%d.org"))

(defvar org-code-capture--store-file "")
(defvar org-code-capture--store-header "")

;; This function is used in combination with a coding template of org-capture.
(defun org-code-capture--store-here ()
  "Register current subtree as a capture point."
  (interactive)
  (setq org-code-capture--store-file (buffer-file-name))
  (setq org-code-capture--store-header (nth 4 (org-heading-components))))

;; This function is used with a capture-template for (function) type.
;; Look for headline that registered at `org-code-capture--store-header`.
;; If the matching subtree is not found, create a new Capture tree.
(defun org-code-capture--find-store-point ()
  "Find registered capture point and move the cursor to it."
  (let ((filename (if (string= "" org-code-capture--store-file)
                      (format-time-string ladicle/org-journal-file-format)
                    org-code-capture--store-file)))
    (set-buffer (org-capture-target-buffer filename)))
  (goto-char (point-min))
  (unless (derived-mode-p 'org-mode)
    (error
     "Target buffer \"%s\" for org-code-capture--find-store-file should be in Org mode"
     (current-buffer))
    (current-buffer))
  (if (re-search-forward org-code-capture--store-header nil t)
      (goto-char (point-at-bol))
    (goto-char (point-max))
    (or (bolp) (insert "\n"))
    (insert "* Capture\n")
    (beginning-of-line 0))
  (org-end-of-subtree))

;; Capture templates for code-reading
(setq org-capture-templates
      '(("code-link"
         "Store the code-reading notes with GitHub and file links for the current cursor position."
         plain
         (function org-code-capture--find-store-point)
         "%^{Summary}\n%(with-current-buffer (org-capture-get :original-buffer) (browse-at-remote-get-url))\n# %a"
         :immediate-finish t)
        ("just-code-link"
         "Immediately store GitHub and file links for the current cursor position to the current code-reading notes."
         plain
         (function org-code-capture--find-store-point)
         "%(with-current-buffer (org-capture-get :original-buffer) (browse-at-remote-get-url))\n# %a"
         :immediate-finish t)))
Code 1: org-captureと関連情報を取得するための関数

各種ソースコードホスティングサービスのリンクを取得

先程のコードでGitHub(だけではなく各種ソースコードホスティングサービス)のURL取得には browse-at-remote を利用している。 (browse-at-remote-prefer-symbolic nil) にしないとハッシュ値ではなくブランチ名が使われるので注意2

(use-package browse-at-remote
  :functions (browse-at-remote-get-url)
  :custom (browse-at-remote-prefer-symbolic nil)
  :bind ("M-g r" . browse-at-remote))
Code 2: browse-at-remoteの設定

他の人にシュッと共有する

書いた内容は自分で見返したり、Slackで共有したり、GitHubのIssueとかに書き込むことがある。 そんな時は、Orgの C-c C-e: (org-export-dispatch) を使っている。

私の場合だと、今後も見返したい内容(subtree)はox-Hugo3でプライベートブログに出力、 社内QiitaやGitHub Issueに書き込みたい時はox-qmd4で一時バッファに出力したものをコピペしている。

デフォルト設定でMarkdown出力するとアンダースコア(_)がした付き文字として認識され <sub> タグが付いてしまう。 これを回避するために、出力オプションに #+OPTIONS: ^:{} を設定すると良い。


  1. ローカルのファイルにジャンプできるOrgリンクのこと。 ↩︎

  2. ブランチ名にするとファイル移動などで追えなくなるため。 ↩︎

  3. ox-hugo - Org to Hugo exporter ↩︎

  4. 0x60df/ox-qmd: Qiita Markdown Back-End for Org Export Engine ↩︎

.