Emacsでインデントとスペースをいいかんじに(align-regexp, my-set-indent-tabs-mode)

githubをうろうろしていたらmoriyamahiroshiさんの.emacsに出会いました。

とても勉強になる内容だったのですが、そのなかに、(my-set-indent-tabs-mode)というものがあり、これを使うと、いいかんじにバッファ先頭のインデントを見て、バッファごとにインデントスタイルを設定できるようでした。

これで、インデントスタイルはメジャーモードごとに決まっていることが多い片方で、フレームワークごとにコーディング規約があったりして、悩ましかったのですが、これは空気を読んでくれるので、なかなかいいかんじでした。

関係部分だけ以下に抜きださせていただきます。

(if (not (fboundp 'defun-if-undefined))
    (defmacro defun-if-undefined (name &rest rest)
      `(unless (fboundp (quote ,name))
         (defun ,name ,@rest))))

(defun-if-undefined inside-string-or-comment-p ()
  (let ((state (parse-partial-sexp (point-min) (point))))
    (or (nth 3 state) (nth 4 state))))

(defun-if-undefined re-search-forward-without-string-and-comments (&rest args)
	(let ((value (apply #'re-search-forward args)))
		(if (and value (inside-string-or-comment-p))
				(apply #'re-search-forward-without-string-and-comments args)
			value)))

(defun my-buffer-indent-tabs-code-p (&optional buffer)
  "Check first indent char."
  (let ((buffer (or buffer (current-buffer))))
    (with-current-buffer buffer
      (save-excursion
        (save-restriction
          (widen)
          (goto-char (point-min))
          (and (re-search-forward-without-string-and-comments "^[ \t]"
                                                              (point-max) t)
               (string= (match-string 0) "\t")))))))

(defun my-set-indent-tabs-mode ()
  (setq indent-tabs-mode (my-buffer-indent-tabs-code-p)))

(add-hook 'emacs-lisp-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'java-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'html-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'perl-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'python-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'ruby-mode-hook #'my-set-indent-tabs-mode)
(add-hook 'sh-mode-hook #'my-set-indent-tabs-mode)

以下は、ついでに僕の設定です。

(add-hook 'php-mode-hook
          '(lambda()
             (setq tab-width 2)
             (setq c-basic-offset 2)
             (my-set-indent-tabs-mode)))

align-regexp

Emacsのalign-regexpは、たいへん便利で、

				$scfldpath = $scfldbase.'modules/'.$name.DS;
				$migrationpath = $scfldpath.'migrations/';
				$configpath = $scfldpath.'config/';
				$viewspath = $scfldpath.'views/'.$name;

こんなのを

M-x align-regexp RET = RET

で、一瞬でこんなのしてくれます。

				$scfldpath     = $scfldbase.'modules/'.$name.DS;
				$migrationpath = $scfldpath.'migrations/';
				$configpath    = $scfldpath.'config/';
				$viewspath     = $scfldpath.'views/'.$name;

で、ちょっと悩ましいのが、fuelphpのコーディング規約みたいなやつ。

すべてのインデントはスペースではなくタブを使用すべきです。
しかし、インデント後の整形はタブではなくスペースを使用します。

Emacsのalign-regexpとしては、indent-tabs-modeの値を見て、インデント後の整形も行うので、indent-tabs-modeがtなら、インデント後の整形も

				$scfldpath			= $scfldbase.'modules/'.$name.DS;
				$migrationpath	= $scfldpath.'migrations/';
				$configpath		= $scfldpath.'config/';
				$viewspath			= $scfldpath.'views/'.$name;

となっちゃうので、fuelphpのコーディング規約から外れちゃう。これをうまいこと解決するのが、以下のadviceです。

(defadvice align-regexp (around advise-align-regexp activate)
  "Let align-regexp indent by spaces."
  (when indent-tabs-mode (setq indent-tabs-mode nil))
  ad-do-it
  (my-set-indent-tabs-mode))

moriyamahiroshiさんの(my-set-indent-tabs-mode)が空気も読んでくれるので、快適です。

ちなみにphpでalign-regexpするには、てつじんにっきさんの「[php][Emacs] Emacsのphp-modeでalignする」の設定も必要です。Emacs24でもいけます。

ジャンル: Emacs