Gistの表示 (xyttr Advent Calendar 4日目)

この記事はxyttr Advent Calendar 2011の記事です。

今回はタイムラインに流れてきたGistの内容をxyzzy内で表示する拡張を書いてみます。

githubGists APIを提供していて、JSON形式で簡単にGistの投稿内容を取得できます。
これを使ってコマンドを作りましょう。

(in-package :xyttr)

;;; カーソル下のGist URLの内容を表示する
(defun show-gist ()
  (interactive)
  ; カーソル下のURLを取得
  (whenlet url (expand-focused-url)
    ; gistのURLだったらIDを取得
    (whenlet id (and (string-match "https://gist\\.github\\.com/\\([0-9]+\\)\\(#.+\\)?$" url)
                     (match-string 1))
      ; Gists APIを使って投稿内容を取得する
      (let ((res (xhr:xhr-get (concat "https://api.github.com/gists/" id)
                              :key #'xhr:xhr-response-text)))
        ; 投稿者名/投稿日時/permalink/投稿内容を抽出
        (w/json (user.login updated_at html_url files) (json:json-decode res)
          ; テンポラリバッファに出力
          (with-output-to-temp-buffer ((format nil "*gist:~A*" id) t t)
            (format t "# ~A~%# ~A [~A]~%" html_url
                    (or user.login "anonymous") updated_at)
            (dolist (f files)
              (w/json (filename content size) (cdr f)
                (format t "# ~A [~A bytes]~%~40@{-~}~%~A~%~%"
                        filename size content)))))))))

(define-key *xyttr-timeline-keymap* '#\g 'show-gist)

実際に表示してみるとこんな感じに。
http://gyazo.com/5fe62085cc964a2e9941cd8ec0e18780.png

上記のコードは投稿内容が複数ファイルでも1バッファに詰めこんで表示します。
ファイルごとにバッファを分けて表示したいなーという場合は次のようにします。

(in-package :xyttr)

;; Gistのファイル1個表示
(defun popup-gist-file (id filename content)
  (let ((buf (get-buffer-create (concat "gist/" id "/" filename))))
    (erase-buffer buf)
    (with-output-to-buffer (buf)
      (princ content))
    (set-buffer buf)
    ;; せっかくなのでメジャーモード設定
    (whenlet mode (assoc filename *auto-mode-alist*
                         :test #'(lambda (fn pat) (string-match pat fn)))
      (funcall (cdr mode)))
    (set-buffer-modified-p nil buf)))

;; コマンド本体
(defun show-gist2 ()
  (interactive)
  (whenlet url (expand-focused-url)
    (whenlet id (and (string-match "https://gist\\.github\\.com/\\([0-9]+\\)\\(#.+\\)?$" url)
                     (match-string 1))
      (let* ((res (xhr:xhr-get (concat "https://api.github.com/gists/" id)
                               :key #'xhr:xhr-response-text))
             (files (json-value (json:json-decode res) files)))
        (dolist (f files)
          (w/json (filename content) (cdr f)
            (popup-gist-file id filename content)))))))

(define-key *xyttr-timeline-keymap* '#\G 'show-gist2)

保存したいなーと思ったらC-x C-n→パス指定→C-x C-s あるいは M-x save-as-dialogから保存するのが簡単です。



以上、Gist表示コマンドの実装例でした。