ククログ

株式会社クリアコード > ククログ > おすすめEmacs設定

おすすめEmacs設定

注: これの更新版である2012年版があります。

他の人がEmacsを使っているのを見ていると、「もっと便利に使えるのに」と、もやっとしたり、「え、その便利な機能ってなに?」と、発見があったりします。だれかに「この設定をすると便利ですよ」と話しやすくするために、今のEmacsのおすすめ設定をここに記しておきます。

ディレクトリ構成

長年漬け込んできたEmacsの設定がそこそこの量になっているので、以下のようなディレクトリ構成にして分類しています。

.emacs.d
|-- init.el         ;; 基本的な設定を記述
|-- config          ;; 特定のモードや非標準のElispの設定をこの下に置く
|   |-- builtins.el ;; 標準Elispの設定
|   |-- packages.el ;; 非標準Elispの設定
|   `-- packages    ;; 非標準Elispのうち、設定が多くなるものはこの下に置く
|       `-- sdic.el ;; (例)非標準Elispであるsdicの設定
`-- packages        ;; 非標準Elispをこの下に置く

.emacs.d/packages/の下には非標準(Emacsに付属していない)Elispをダウンロードします。これは後述するように自動化しているので、新しい環境でも、.emacs.d/init.elと.emacs.d/config/以下だけあれば同じ環境を構築できます。アップデートしたい場合は単純に.emacs.d/packages/を削除してEmacsを再起動するだけなので手間がかかりません。

init.el: 基本的な設定

それでは、まず、基本的な設定を説明します。

ロードパス

;;; ロードパスの追加
(setq load-path (append
                 '("~/.emacs.d"
                   "~/.emacs.d/packages")
                 load-path))

日本語環境

;;; Localeに合わせた環境の設定
(set-locale-environment nil)

キーバインド

;;; キーバインド
(define-key global-map (kbd "C-h") 'delete-backward-char) ; 削除
(define-key global-map (kbd "M-?") 'help-for-help)        ; ヘルプ
(define-key global-map (kbd "C-z") 'undo)                 ; undo
(define-key global-map (kbd "C-c i") 'indent-region)      ; インデント
(define-key global-map (kbd "C-c C-i") 'hippie-expand)    ; 補完
(define-key global-map (kbd "C-c ;") 'comment-dwim)       ; コメントアウト
(define-key global-map (kbd "C-o") 'toggle-input-method)  ; 日本語入力切替
(define-key global-map (kbd "M-C-g") 'grep)               ; grep
(define-key global-map (kbd "C-[ M-C-g") 'goto-line)      ; 指定行へ移動

便利なのがM-C-gのgrepです。grepにはまだ設定があります。

grep

;;; 再帰的にgrep
(require 'grep)
(setq grep-command-before-query "grep -nH -r -e ")
(defun grep-default-command ()
  (if current-prefix-arg
      (let ((grep-command-before-target
             (concat grep-command-before-query
                     (shell-quote-argument (grep-tag-default)))))
        (cons (if buffer-file-name
                  (concat grep-command-before-target
                          " *."
                          (file-name-extension buffer-file-name))
                (concat grep-command-before-target " ."))
              (+ (length grep-command-before-target) 1)))
    (car grep-command)))
(setq grep-command (cons (concat grep-command-before-query " .")
                         (+ (length grep-command-before-query) 1)))

-rオプションを追加して常に再帰的にgrepするようにします。grep-findなどを使い分けなくてもすみます。

画像表示

;;; 画像ファイルを表示
(auto-image-file-mode t)

バッファ内で画像ファイルを表示します。

バーを消す

;;; メニューバーを消す
(menu-bar-mode -1)
;;; ツールバーを消す
(tool-bar-mode -1)

カーソル

;;; カーソルの点滅を止める
(blink-cursor-mode 0)

eval

;;; evalした結果を全部表示
(setq eval-expression-print-length nil)

括弧

;;; 対応する括弧を光らせる。
(show-paren-mode 1)
;;; ウィンドウ内に収まらないときだけ括弧内も光らせる。
(setq show-paren-style 'mixed)

昔はmic-paren.elも使っていましたが、標準の機能で十分なので、もう使っていません。

空白

;;; 行末の空白を表示
(setq-default show-trailing-whitespace t)

位置

;;; 現在行を目立たせる
(global-hl-line-mode)

;;; カーソルの位置が何文字目かを表示する
(column-number-mode t)

;;; カーソルの位置が何行目かを表示する
(line-number-mode t)

;;; カーソルの場所を保存する
(require 'saveplace)
(setq-default save-place t)

;;; 行の先頭でC-kを一回押すだけで行全体を消去する
(setq kill-whole-line t)

;;; 最終行に必ず一行挿入する
(setq require-final-newline t)

;;; バッファの最後でnewlineで新規行を追加するのを禁止する
(setq next-line-add-newlines nil)

バックアップ

;;; バックアップファイルを作らない
(setq backup-inhibited t)

;;; 終了時にオートセーブファイルを消す
(setq delete-auto-save-files t)

補完

;;; 補完時に大文字小文字を区別しない
(setq completion-ignore-case t)
(setq read-file-name-completion-ignore-case t)

;;; 部分一致の補完機能を使う
;;; p-bでprint-bufferとか
(partial-completion-mode t)

;;; 補完可能なものを随時表示
;;; 少しうるさい
(icomplete-mode 1)

履歴

;;; 履歴数を大きくする
(setq history-length 10000)

;;; ミニバッファの履歴を保存する
(savehist-mode 1)

;;; 最近開いたファイルを保存する数を増やす
(setq recentf-max-saved-items 10000)

圧縮

;;; gzファイルも編集できるようにする
(auto-compression-mode t)

diff

;;; ediffを1ウィンドウで実行
(setq ediff-window-setup-function 'ediff-setup-windows-plain)

;;; diffのオプション
(setq diff-switches '("-u" "-p" "-N"))

ディレクトリ

;;; diredを便利にする
(require 'dired-x)

;;; diredから"r"でファイル名をインライン編集する
(require 'wdired)
(define-key dired-mode-map "r" 'wdired-change-to-wdired-mode)

ファイル名をそのまま変更できるのは便利です。

バッファ名

;;; ファイル名が重複していたらディレクトリ名を追加する。
(require 'uniquify)
(setq uniquify-buffer-name-style 'post-forward-angle-brackets)

追加の設定をロード

最後にconfig/以下に置いてある設定ファイルを読み込みます。

;;; 標準Elispの設定
(load "config/builtins")

;;; 非標準Elispの設定
(load "config/packages")

config/builtins.el: 標準Elispの設定

config/builtins.elには標準Elisp(Emacsに付属しているElisp)の設定を記述します。まだ整理しきれていないので、1つだけ紹介します。

バージョン管理システム

diredで"V"を入力するとそのディレクトリで使っているバージョン 管理システム用のモードを起動します。

(defun dired-vc-status (&rest args)
  (interactive)
  (cond ((file-exists-p (concat (dired-current-directory) ".svn"))
         (svn-status (dired-current-directory)))
        ((file-exists-p (concat (dired-current-directory) ".git"))
         (magit-status (dired-current-directory)))
        (t
         (message "version controlled?"))))

(define-key dired-mode-map "V" 'dired-vc-status)

config/packages.el: 非標準Elispの設定

config/packages.elには非標準Elispの設定を記述します。しかし、非標準なので、設定をする前にダウンロードしてくる必要があります。Elispを管理するElispはいくつかあるのですが、以下のような動作をするものがないため、簡単なものを自作しています。

  • すでにインストールしていたら何もせずに、なかったらインストールして欲しい。明示的にS式を評価したくない。コメントにインストールS式を残すのではなく、コメントにせずに残したい。
  • 非同期ではなく同期でインストールして欲しい。Emacsを起動して、すでにインストールされていなかったらその場でインストールして、インストールが終わったらいつも通り使える状態になって欲しい。インストールしている時間くらい待てるので、がんばって非同期でインストールして、「中途半端に動く状態」だけど「素早く起動」しなくてもよい。多少初回起動時に遅くても、動いたら完全に動く状態になってくれた方がよい。

パッケージ管理システム

config/packages.elの先頭に以下のような簡単なパッケージ管理システムを定義しています。以下の場所にあるElispをインストールできます。

インストールしたパッケージは~/.emacs.d/packages/以下にインストールされます。

(require 'cl)

(defvar package-base-dir "~/.emacs.d/packages")

(defun package-path-basename (path)
  (file-name-sans-extension (file-name-nondirectory path)))

(defun package-directory (files)
  (concat package-base-dir "/"
          (package-path-basename (car files))))

(defun package-run-shell-command (command)
  (message (format "running...: %s" command))
  (shell-command command))

(defun package-install-from-emacswiki (files)
  (shell-command
   (format "mkdir -p %s" (package-directory files)))
  (package-run-shell-command
   (format "wget --directory-prefix %s %s"
           (package-directory files)
           (mapconcat (lambda (name)
                        (concat "http://www.emacswiki.org/emacs/download/"
                                name))
                      files
                      " "))))

(defun package-install-from-github (files)
  (package-run-shell-command
   (format (concat "git clone https://github.com/%s.git %s")
           (car files)
           (package-directory files))))

(defun package-install-from-repo.or.cz (files)
  (package-run-shell-command
   (format (concat "git clone git://repo.or.cz/%s.git %s")
           (car files)
           (package-directory files))))

(defun package-alist-value (alist key default-value)
  (if (listp alist)
      (let ((alist-item (assoc key alist)))
        (if alist-item
            (cdr alist-item)
          default-value))
    default-value))

(defun package-install (type package-spec require-name &optional force)
  (let ((files (package-alist-value package-spec 'files
                                    (if (listp package-spec)
                                        package-spec
                                      (list package-spec))))
        (base-path (package-alist-value package-spec 'base-path "."))
        (additional-paths (package-alist-value package-spec 'additional-paths
                                               nil))
        (install-proc (case type
                        (emacswiki
                         'package-install-from-emacswiki)
                        (github
                         'package-install-from-github)
                        (repo.or.cz
                         'package-install-from-repo.or.cz)
                        (t
                         (error "unknown package type: <%s>(%s)"
                                type package)))))
    (add-to-list 'load-path
                 (format "%s/%s"
                         (package-directory files)
                         base-path))
    (dolist (additional-path additional-paths)
      (add-to-list 'load-path (format "%s/%s"
                                      (package-directory files)
                                      additional-path)))
    (condition-case err
        (require require-name)
      (error
       (message (format "installing %s..." files))
       (funcall install-proc files)))
    (require require-name)))

grep-edit: grep結果をインラインで編集

grepの結果を直接編集できるようになります。wdiredと合わせてC-c C-cでも編集結果を反映できるようにしています。

;;; *grep*で編集できるようにする
(package-install 'emacswiki "grep-edit.el" 'grep-edit)
(add-hook 'grep-setup-hook
          (lambda ()
            (define-key grep-mode-map (kbd "C-c C-c") 'grep-edit-finish-edit)))

Auto Complete: 自動補完

自動で補完候補をだしてくて便利です。補完候補をC-n/C-pでも選択できるようにしています。

;;; 自動補完
(package-install 'github "m2ym/auto-complete" 'auto-complete-config)
(add-to-list 'ac-dictionary-directories
             (format "%s/auto-complete/dict" package-base-dir))
(ac-config-default)
(add-hook 'auto-complete-mode-hook
          (lambda ()
            (define-key ac-completing-map (kbd "C-n") 'ac-next)
            (define-key ac-completing-map (kbd "C-p") 'ac-previous)))

Anything

いろいろ便利に使えるらしいAnythingですが、iswitchb-modeの代わりにだけ使っています。isiwtchb-modeの代わりなのでキーバインドはC-x bだけです。

;;; Anything
(let ((original-browse-url-browser-function browse-url-browser-function))
  (setq anything-command-map-prefix-key "C-c C-<SPC>")
  (package-install 'repo.or.cz '((files . ("anything-config"))
                                 (additional-paths . ("extensions")))
                   'anything-startup)
  (define-key global-map (kbd "C-x b") 'anything-for-files)
  (define-key anything-map (kbd "C-z") nil)
  (define-key anything-map (kbd "C-l") 'anything-execute-persistent-action)
  (define-key anything-map (kbd "C-o") nil)
  (define-key anything-map (kbd "C-M-n") 'anything-next-source)
  (define-key anything-map (kbd "C-M-p") 'anything-previous-source)
  (setq browse-url-browser-function original-browse-url-browser-function))

run-test: テスト実行

C-x C-tで近くにあるrun-test.shやrun-test.rbという名前のファイルを実行するツールです。

;;; テスト実行
(package-install 'github '((files . ("kou/run-test"))
                           (base-path . "lib"))
                 'run-test-setting)

まとめ

長年漬け込んできたEmacsの設定を紹介しました。

ここで紹介した内容はGitHubに置いておいたので、興味がある人は試してみてください。