🦬

Emacs で Zenn への投稿・更新を簡単にする

2022/07/16に公開

はじめに

Zenn は zenn-cli があるおかげで記事が書きやすい。しかし、それでも慣れてくるとちょっとした作業が面倒になってくる。たとえば Typo を修正したいとき、

  1. 記事のディレクトリに移動する
  2. 該当の Markdown ファイルを探す (slugがハッシュだとファイル名からは推測つかない)
  3. プレビュー用のサービスを起動する (すでに起動してるのにまた起動したりしてしまう)
  4. ブラウザから該当の記事をまた探す (だいたいいちばん上にあるけど二度手間)

となってまずファイルを探すのが面倒で確認はいいからプッシュしちゃえとなりがちだった。そこで Emacs からより簡単に使えるようにした。

どこからでもいいので C-j z をタイプすると次のようにメニューが出る。

あとはやりたいことを選択するだけ。

各操作

n: 新規記事

  • zenn new:article を実行し、ファイルを開く

s: 検索


s のあと rust と入力したところ

  • helm-ag を使って title: で絞り込んだ状態で起動する
  • そのまま選んでもいいし、さらに絞り込んでもいい
  • 本文検索なら title: を消す

d: Dired

  • 記事ディレクトリを開く

p: プレビュー

  • 開いている Markdown ファイルに対応するローカルのURLを直接開く
  • プレビュー用のサービスが起動してなかったら自動で起動させるのでプレビュー用のサービスへの関心が不要になる

u: アップロード

  • git add -A; git now; git push を実行する

o: 本家で開く

  • 開いている Markdown ファイルに対応する本家のURLを開く

i: パッケージ更新

  • npm install zenn-cli@latest を実行する
  • 更新を求められたとき用

コード

(require 'transient)
(require 'helm-ag)
(require 'f)

(transient-define-suffix my-zenn-article-new ()
  :key "n"
  :description "新規記事"
  (interactive)
  (let ((slug (shell-command-to-string "ruby -r securerandom -e 'SecureRandom.hex(7).display'")))
    (shell-command (format "cd ~/src/zenn-content && zenn new:article --slug %s" slug))
    (find-file (format "~/src/zenn-content/articles/%s.md" slug))
    (end-of-buffer)))

(transient-define-suffix my-zenn-search ()
  :key "s"
  :description "検索"
  (interactive)
  (helm-do-ag "~/src/zenn-content" nil "title: "))

(transient-define-suffix my-zenn-dired ()
  :key "d"
  :description "Dired"
  (interactive)
  (dired "~/src/zenn-content/articles"))

(transient-define-suffix my-zenn-current-open ()
  :key "o"
  :description "本家で開く"
  (interactive)
  (when buffer-file-name
    (let* ((stem (f-base buffer-file-name))
           (url (format "https://zenn.dev/megeton/articles/%s" stem)))
      (shell-command (format "open %s" url)))))

(transient-define-suffix my-zenn-current-preview ()
  :key "p"
  :description "プレビュー"
  (interactive)
  (let* ((stem (f-base buffer-file-name))
         (url (format "http://localhost:8001/articles/%s" stem)))
    (my-zenn-preview-start)
    (shell-command (format "open %s" url))))

(transient-define-suffix my-zenn-preview-start ()
  :key "1"
  :description "プレビュー起動"
  (interactive)
  (when (not (process-status "zenn"))
    (start-process-shell-command "zenn" "*Zenn*" "cd ~/src/zenn-content && zenn preview -p 8001")))

(transient-define-suffix my-zenn-preview-stop ()
  :key "0"
  :description "プレビュー停止"
  (interactive)
  (when (process-status "zenn")
    (delete-process "zenn")))

(transient-define-suffix my-zenn-upload ()
  :key "u"
  :description "アップロード"
  (interactive)
  (async-shell-command "cd ~/src/zenn-content && git add -A; git now; git push"))

(transient-define-suffix my-zenn-install ()
  :key "i"
  :description "パッケージ更新"
  (interactive)
  (async-shell-command "cd ~/src/zenn-content && npm install zenn-cli@latest"))

(transient-define-prefix my-zenn-menu ()
  "Zenn"
  [["Basic"
    (my-zenn-article-new)
    (my-zenn-search)
    (my-zenn-dired)
    ]
   ["Preview"
    (my-zenn-current-preview)
    (my-zenn-upload)
    (my-zenn-current-open)
    ]
   ["Other"
    (my-zenn-preview-start)
    (my-zenn-preview-stop)
    (my-zenn-install)
    ]])

;; (global-set-key (kbd "C-j z") 'my-zenn-menu)

Discussion