動機

Emacsのカスタマイズをした。Blogを書くときは自作のEditorモードを用いて いる。これは書いた内容を特定のディレクトリに配置してGitに登録するツー ルだ。使ってくと2つのパターンでファイルを作成したくなることに気づいた。 今回はそれぞれのパターンに対応できるようにEmacsをカスタマイズした。

2つのパターン

ファイルスタイル

今までのスタイルでXXXX.orgのようにURLとして用いられるファイル名に直接 拡張子がついたものである。これを便宜的にファイルスタイルと呼ぶことにす る。

例えば短文のコメントであったり、複数のファイルを作る必要のないようなシ ンプルな記事の場合に用いられる。

ディレクトリスタイル

これはXXXX/index.orgのようにURLとして用いられる部分がディレクトリ名に なっており、その配下にindex.orgというファイルを作成するものである。こ れを便宜的にディレクトリスタイルと呼ぶことにする。

例えばサンプルコードとして別ファイルを作成したり、画像を配置したりする 場合に用いられる。

2つのパターンに対応する

主にeditor-save-as-kill関数を修正しeditor-file-path-directory-styleの 値で挙動を変更できるようにした。コマンドUIとしてtransientを用いている ので、fでファイルスタイル、dでディレクトリスタイル、sでデフォルトのス タイルとして保存できるように修正した。

全文を載せておく。

(defvar editor-buffer-name "*EDITOR*")

(defvar editor-map (make-sparse-keymap))

(defun editor-refresh-export-option-date ()
  "DATEエクスポートオプションの更新"
  (interactive)
  (let* ((timestamp (format-time-string "%+FT%T%z"))
	 (pattern (format "s/^\#+DATE:.*$/#+DATE: %s/g" timestamp)))
    (call-process-region (point-min) (point-max) "sed" t t t "-e" pattern)))


(defun editor-create-buffer ()
  (interactive)
  (let ((buf-name editor-buffer-name))
    (with-current-buffer (get-buffer-create buf-name)
      (if (= 0 (buffer-size))
	  (progn
	    ;; エクスポートオプションの追加
	    (save-excursion
              (goto-char 0)
              (insert "#+DATE:\n#+TAGS[]: comment\n\n"))

	    (editor-refresh-export-option-date)))
      (kill-all-local-variables)
      (use-local-map editor-map)
      (editor-mode))
    (switch-to-buffer buf-name)))

(define-derived-mode editor-mode org-mode
  "Editor mode"
  nil)


(defun editor-make-new-file-path ()
  "エディターモードの保存先ファイルのパス返す。

通常ではファイルスタイルorgファイル (XXXX.org) のパスを返す。
`editor-file-path-directory-style` をNONE NILにするとディレクトリスタ
イルのパス(XXXX/index.org)を返す。
"
  (let ((file-style-path (concat (directory-file-name editor-base-directory)
				 (format "/%s.org" (truncate (float-time))))))
    (if editor-file-path-directory-style
	(concat (directory-file-name (file-name-sans-extension file-style-path)) "/index.org")
      file-style-path)))

(defcustom editor-base-directory "/ng/symdon/pages/posts")
(defcustom editor-file-path-directory-style nil)
(defcustom editor-new-file-path #'editor-make-new-file-path)

(defun editor-save-as-kill ()
  "エディターバッファの内容をファイルに保存してgit commitする"
  (interactive)
  (let ((new-file-path (funcall editor-new-file-path)))

    ;; Create parent directory.
    (make-directory (file-name-directory new-file-path) t)

    ;; Copy buffer content
    (switch-to-buffer
     (with-current-buffer (find-file-noselect new-file-path)
       (insert-buffer-substring (get-buffer editor-buffer-name))
       (save-buffer)
       (current-buffer)))

    ;; Git commit
    (let ((default-directory (file-name-directory new-file-path)))
      (shell-command (format "git add %s" new-file-path))
      (shell-command (format "git commit -m 'Add comment.' %s" new-file-path))))

  (kill-buffer editor-buffer-name))


(defun editor-save-as-kill-file-style ()
  "ファイルスタイルでエディターバッファの内容を保存する"
  (interactive)
  (let ((editor-file-path-directory-style nil))
    (editor-save-as-kill)))

(defun editor-save-as-kill-directory-style ()
  "ディレクトリスタイルでエディターバッファの内容を保存する"
  (interactive)
  (let ((editor-file-path-directory-style t))
    (editor-save-as-kill)))

(transient-define-prefix editor-save-as ()
  "Editor mode save as..."
  ["Save as"
   ("f" "Save as file style" editor-save-as-kill-file-style)
   ("d" "Save as directory style" editor-save-as-kill-directory-style)
   ("s" "Save as default" editor-save-as-kill)
   ])  

(bind-keys :map editor-mode-map
	   ("C-x C-s" . editor-save-as))

(bind-key* "C-t C-w" 'editor-create-buffer)