この記事はEmacs Advent Calendar 2017及びしむどん Advent Calendar 2017の20日目の記事です。

wakatime

https://wakatime.com/

プログラミングの作業時間を自動的に収集してグラフ化してくれます。org-modeなどでタスクの時間計測をしている方も多いと思いますが、wakatimeではファイルがあるディレクトリからリポジトリなどの情報を用いてプロジェクトごとの時間を計測します。

Emacsでwakatimeを使う

https://github.com/wakatime/wakatime-mode

wakatimeには各種エディタやブラウザ向けにプラグインが提供されています。Emacsにはwakatime-modeが提供されています。wakatime-mode自体はMELPAからインストールできます。

wakatime-modeの内部ではCLIコマンドのwakatimeを利用しているためそちらもインストールする必要があります。これはPython製のコマンドのためpipでインストールします。

pip install wakatime

設定

wakatimeの設定は.wakatime.cfgに記述します。 `api_key` にwakatimeのサイトで発行したapi_keyを設定します。

[settings]
debug = false
api_key = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
hidefilenames = false
exclude =
    ^COMMIT_EDITMSG$
    ^TAG_EDITMSG$
    ^/var/(?!www/).*
    ^/etc/
include =
    .*
offline = true
timeout = 30
$HOME/.wakatime.cfg

あとは.emacs.d/init.elに次を記述します。

(global-wakatime-mode t)

この状態でgitリポジトリ内のファイルを開くとリポジトリをプロジェクトと見立てて時間計測をしてくれます。

ファイルのないものに対して計測を行う

上記であればプロジェクトの時間計測を行ってくれますが、ファイルがないもの(例えばtwittering-modeなど)では集計してくれませんでした。ファイルじゃないバッファの場合もプロジェクトとして計測したかったので、バッファ名をプロジェクトとしてみたてるようにカスタマイズして使っています。

wakatime-client-commandはwakatimeコマンドを生成するための関数です。これを上書きしてファイル名がなかったらバッファ名を用いるように変更しています。

(defun wakatime-client-command (savep)
  "Return client command executable and arguments.
 Set SAVEP to non-nil for write action."
  (format "%s%s--file \'%s\' %s --plugin \"%s/%s\" --time %.2f%s%s"
          (if (s-blank wakatime-python-bin) "" (format "%s " wakatime-python-bin))
          (if (s-blank wakatime-cli-path) "wakatime " (format "%s " wakatime-cli-path))
          (or (buffer-file-name (current-buffer)) (buffer-name))
          (if (buffer-file-name (current-buffer)) "" (format " --entity-type app --project \'%s\' " mode-name))
          wakatime-user-agent
          wakatime-version
          (float-time)
          (if savep " --write" "")
          (if (s-blank wakatime-api-key) "" (format " --key %s" wakatime-api-key))))

(defun wakatime-save ()
  "Send save notice to WakaTime."
  (wakatime-call t))

(defun wakatime-bind-hooks ()
  "Watch for activity in buffers."
  (add-hook 'after-save-hook 'wakatime-save nil t)
  (add-hook 'auto-save-hook 'wakatime-save nil t)
  (add-hook 'first-change-hook 'wakatime-ping nil t))

(defun wakatime-unbind-hooks ()
  "Stop watching for activity in buffers."
  (remove-hook 'after-save-hook 'wakatime-save t)
  (remove-hook 'auto-save-hook 'wakatime-save t)
  (remove-hook 'first-change-hook 'wakatime-ping t))