« ^ »

Editorモードで作成するファイルをファイルスタイルとディレクトリスタイルの両方に対応した

所要時間: 約 3分

動機

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)