目次

導入

Alt Text

趣味の俳句やプログラミングの備忘録、孫の成長記録など、様々なことをWebで発信したり記録として残しておくのが好きな私は、たくさんのブログをWordPressで運営していました。けれどもWebページを開いてチマチマとしたスペースに記事を書き込み、やおら更新しては確認するというプロセスが面倒くさく、ついつい億劫になってだんだん書かなくなるという悪循環。そんな葛藤の中でhugoと出会い、その快適さに取り憑かれてしまい、運用していた複数のWordPressを全てhugoに移行しました。


以前に、Emacsで複数のhugoブログを管理する という記事を書きましたが、そのときにヒントを頂いたブログ記事の作者が、easy-hugo.el を開発されているのを知って試してみました。

自動化が大好きな僕には大いに刺激的なLispです。easy-hugo.elはパッケージでも提供されているのでインストールも簡単です。これをベースにして複数ブログを切り替えて使えるように以下の設定ファイルを書きました。

リモートサーバーには、sshで接続できるようにあらかじめ設定しておきます。

拡張設定のポイント

  1. 複数のブログをメニューから切り替えて使えるようにする。(後日のupdateで対応していただきました)
  2. ファイル名をいちいち考えるのが面倒なのでタイムスタンプをファイル名として自動付与させる。
  3. 記事のURL(パーマリンク)は、[baseurl]/201705212142/のような12桁の数字になるのが好きなので、こちらもタイムスタンプが自動挿入されるようにしている。
  4. こうすることで、new blog postを選ぶだけでいきなり記事がかける。
  5. ファイル名をタイムスタンプにしておけば、easy-hugo-sort-charすることで投稿順の時系列に並べることができる。

設定ファイル

(use-package easy-hugo
  :init
  ;; Charactor-sort at s key
  (setq easy-hugo-sort-default-char 1)
  :config
  ;; Sort-char at startup
  (setq easy-hugo--sort-char-flg 2)
  (setq easy-hugo--sort-time-flg nil)
  ;; No help-mode from startup
  ;; (setq easy-hugo-no-help t)
)

;; Main blog
(setq easy-hugo-basedir "~/dropbox/web/textgh/snap/")
(setq easy-hugo-url "http://snap.textgh.org")
(setq easy-hugo-sshdomain "xsrv")
(setq easy-hugo-root "/home/minorugh/textgh.org/public_html/snap/")

;; total number of blogs
(setq easy-hugo-blog-number 4)
;; Blog1
(setq easy-hugo-basedir-1 "~/dropbox/web/textgh/blog/")
(setq easy-hugo-url-1 "http://blog.textgh.org")
(setq easy-hugo-sshdomain-1 "xsrv")
(setq easy-hugo-root-1 "/home/minorugh/textgh.org/public_html/blog/")
;; Blog2
(setq easy-hugo-basedir-2 "~/dropbox/web/textgh/snap/")
(setq easy-hugo-url-2 "http://snap.textgh.org")
(setq easy-hugo-sshdomain-2 "xsrv")
(setq easy-hugo-root-2 "/home/minorugh/textgh.org/public_html/snap/")
;; Blog3
(setq easy-hugo-basedir-3 "~/dropbox/web/textgh/hl/")
(setq easy-hugo-url-3 "http://hl.textgh.org")
(setq easy-hugo-sshdomain-3 "xsrv")
(setq easy-hugo-root-3 "/home/minorugh/textgh.org/public_html/hl/")
;; Blog4
(setq easy-hugo-basedir-4 "~/dropbox/web/textgh/essay/")
(setq easy-hugo-url-4 "http://essay.textgh.org")
(setq easy-hugo-sshdomain-4 "xsrv")
(setq easy-hugo-root-4 "/home/minorugh/textgh.org/public_html/essay/")

;; Key-binding
(key-chord-define-global "hh" 'easy-hugo)
(bind-key "C-c p" 'easy-hugo-preview )
(bind-key "C-c P" 'easy-hugo-publish )


;****************************************
;; Customize
;****************************************
;; Help menu
(defconst easy-hugo--help
"n ... New blog post    s ... Sort time            a ... Search with helm-Ag
p ... Preview          S ... Sort charactor       d ... Delete post
v ... Open view-mode   r ... Refesh               O ... Open basedir
o ... Open edit-mode   1 ... blog.extgh.org       3 ... hl.textgh.org
P ... Publish server   2 ... snap.textgh.org      4 ... essay.textgh.Org

")

;; hugo-replace-key function
(defun hugo-replace-key (key val)
(save-excursion
(goto-char (point-min))
; quoted value
(if (and (re-search-forward (concat key " = \"") nil t)
           (re-search-forward "[^\"]+" (line-end-position) t))
    (or (replace-match val) t) ; ensure we return t
  ; unquoted value
  (when (and (re-search-forward (concat key " = ") nil t)
             (re-search-forward ".+" (line-end-position) t))
    (or (replace-match val) t)))))

;; Automatic input filename and hyperlink as timestamp
(defun easy-hugo-newpost (post-file)
  (interactive (list (read-from-minibuffer "Filename: "
  		     (concat (format-time-string "%Y%m%d%H%M") easy-hugo-default-ext))))
  (let ((filename (concat "post/" post-file))
        (file-ext (file-name-extension post-file)))
    (easy-hugo-with-env
     (when (file-exists-p (file-truename (concat "content/" filename)))
       (error "%s already exists!" (concat easy-hugo-basedir "content/" filename)))
     (call-process "hugo" nil "*hugo*" t "new" filename)
     (find-file (concat "content/" filename))
     (hugo-replace-key "url" (format-time-string "\/%Y%m%d%H%M")) 
     (goto-char (point-max))
     (save-buffer))))

;; Change default-directory
(add-hook 'easy-hugo-mode-hook
      (lambda()
        (setq-local default-directory "~/")))

;; Switch to other-buffer after quit.
(defun easy-hugo-quit ()
  "Quit easy hugo."
  (interactive)
  (easy-hugo--preview-end)
  (when (buffer-live-p easy-hugo--mode-buffer)
    (kill-buffer easy-hugo--mode-buffer))
    (switch-to-buffer(other-buffer)))

;; Key binding
(define-key easy-hugo-mode-map "1" 'easy-hugo-1) ;; blog.textgh.org
(define-key easy-hugo-mode-map "2" 'easy-hugo-2) ;; snap.textgh.org
(define-key easy-hugo-mode-map "3" 'easy-hugo-3) ;; hl.textgh.org
(define-key easy-hugo-mode-map "4" 'easy-hugo-4) ;; essay.textgh.org

Easy-hugoは素晴らしい。

初期のバージョンでは、複数ブログを切り替えるために、easy-hugoを一度終了して再度起動させる必要がありました。 その後、このブログを見てくださった作者のmasasamが、複数ブログ対応のためにupdateを繰り返してくださって、完成度の高い素晴らしい機能にしていただきました。

emacs lispの難しい知識は何も知らずに、いろんなTipsを探してはパッチワークしていたビギナーのぼくにとって、その過程はとても新鮮で良い学びの機会になりました。私と同じようなレベルの方にとっても参考になると考えて、updateとその対応過程を後日記として残しておこうと思います。

ご指導いただいたmasasamさんに心から感謝します。



後日記

作者のmasasamさんから直接指導いただいたことや、easy-hugo.elのupdateに伴い追加変更した経過を書き留めています。

easy-hugo–help(custom-menu)を変更

easy-hugo–help が分割され、no-helpを選んだ場合でもドメインの表示は残るようにupdateしてくださったので、自分の環境用に合わせてeasy-hugo–helpを以下のように再定義しています。(2017.05.30)

(defconst easy-hugo--help
"n ... New blog post    s ... Sort time            a ... Search with helm-Ag
p ... Preview          S ... Sort charactor       d ... Delete post
v ... Open view-mode   r ... Refesh               b ... Open basedir
o ... Open edit-mode   1 ... blog.extgh.org       3 ... hl.textgh.org
P ... Publish server   2 ... snap.textgh.org      4 ... essay.textgh.Org

")

複数ブログ対応にupdateされました。!

easy-hugo.elが複数ブログ対応にupdateされたので、設定を修正しました。(2017.5.31)

記事編集中でもプレビューを起動出来る

新規投稿や記事編集を選んだ場合でも、*Easy-hugo*のbufferは生きているので、キーバインドを設定しておけばどの場面からでもプレビューを起動できます。編集しながらリアルタイムでプレビュー出来るのはなにげに便利です。勿論、Publishも問題ありません。

;; Preview & Publish (bind-keyを使った設定)
(bind-key "C-c p" 'easy-hugo-preview)
(bind-key "C-C P" 'easy-hugo-publish)

リスト表示のデフォルトをファイル名にする

好みの問題ですが、ファイルは更新のたびにタイムスタンプが変わるので、sort-timeだと最新の更新が常に最上部(または最下部)になります。自分はファイルを編集しても投稿順に並べておきたいので、ファイル名をタイムスタンプにしておいてファイル名でソートするようにしています。作者から以下の設定を教えていただきました。

;; 初期値の昇順なら
(setq easy-hugo--sort-char-flg 1)
(setq easy-hugo--sort-time-flg nil)

;; 初期値の降順なら
(setq easy-hugo--sort-char-flg 2)
(setq easy-hugo--sort-time-flg nil)

easy-hugoからdiredでbasedirを開く

defaultでhugoの設定ファイルを編集するために、Open configというコマンドが用意されていましたが、config.toml以外にも、cssやthemeなどを編集することも多いので、Open-basedir コマンドを用意していただきました。

(defun easy-hugo-open-basedir ()
  "Open easy-hugo-basedir."
  (interactive)
  (switch-to-buffer (find-file-noselect easy-hugo-basedir))

ごく個人的な設定

※ Automatic input filename and hyperlink as timestamp.

new-postのとき、ファイル名を考えるのが面倒なので12桁のタイムスタンプを自動的に付与されるようにしています。それに合わせて、blog記事のハイパーリンクも同じ12桁になるようにしています。

;; hugo-replace-key function
(defun hugo-replace-key (key val)
(save-excursion
(goto-char (point-min))
; quoted value
(if (and (re-search-forward (concat key " = \"") nil t)
           (re-search-forward "[^\"]+" (line-end-position) t))
    (or (replace-match val) t) ; ensure we return t
  ; unquoted value
  (when (and (re-search-forward (concat key " = ") nil t)
             (re-search-forward ".+" (line-end-position) t))
    (or (replace-match val) t)))))

;; Automatic input filename and hyperlink as timestamp
(defun easy-hugo-newpost (post-file)
  (interactive (list (read-from-minibuffer "Filename: "
  		     (concat (format-time-string "%Y%m%d%H%M") easy-hugo-default-ext))))
  (let ((filename (concat "post/" post-file))
        (file-ext (file-name-extension post-file)))
    (easy-hugo-with-env
     (when (file-exists-p (file-truename (concat "content/" filename)))
       (error "%s already exists!" (concat easy-hugo-basedir "content/" filename)))
     (call-process "hugo" nil "*hugo*" t "new" filename)
     (find-file (concat "content/" filename))
     (hugo-replace-key "url" (format-time-string "\/%Y%m%d%H%M")) 
     (goto-char (point-max))
     (save-buffer))))

※ Switch to other-buffer after quit.

‘q’でEasy-hugoを終了したときになぜが、*Messages*バッファーを開くので、Easy-hugoの前に開いていたbufferに戻るようにしました。ぼくの環境(Macbook)だけなのかもしれないのですが…

;; Switch to other-buffer after quit.
(defun easy-hugo-quit ()
  "Quit easy hugo."
  (interactive)
  (easy-hugo--preview-end)
  (when (buffer-live-p easy-hugo--mode-buffer)
    (kill-buffer easy-hugo--mode-buffer))
    (switch-to-buffer(other-buffer)))


※ Change default-directory

Easy-hugoの起動デレクトリは basedirになっています。当然ながら複数ブログを切り替えても起動デレクトリは変わりません。ぼくの場合モードラインにbufferのデレクトリも表示させているので勘違いしないためにEasy-hugoの起動デレクトリは、”~/“になるように設定で変更しています。

;; Change default-directory
(add-hook 'easy-hugo-mode-hook
      (lambda()
        (setq-local default-directory "~/")))


なぜeasy-hugoが素晴らしいのか

徹底的に自動化を目指しているところが素晴らしいのです。作者のコンセプトも同じだと思うのですが、ブログ書きの環境は、”書こう!” という意慾が萎えないために、限りなく手間がかからず軽快かつスピーディーであることがとても重要です。そのニーズを120%満たしてくれるのがeasy-hugo.elです。


※記事内の参考codeは、それぞれ最新update版で動作確認済みです。