条件分岐 - 環境設定のための Emacs Lisp 入門

Emacs Lisp 入門の第 8 回です。
設定に絞った Emacs Lisp の入門なので、制御構造は必要ないかと思い、前回で終了していました。 しかし、 OS など環境の違う場合に同じ設定ファイルを使おうとすると、条件分岐は必要になってきます。 そこで、今回、 elisp での条件分岐について追加しました。

条件分岐の式

条件分岐の式の基本は if です。
 (if 条件式
     真の式
   偽の式1
   偽の式2
    :)
条件式は真ならば式の 2 番目の引数として渡した式が評価され、 偽ならば 3 番目以降が順に評価されます。


ただし、 if は偽の時は複数書けるのですが、真で評価する式には 1 つしか書けません。 こういった場合、 progn で複数書けます。
 (if 条件式
     (progn 
       式1
       式2
       :))
ただ、ちょっと書くのが面倒なので、真の時だけ渡した式を順に評価する when も用意されています。
 (when 条件式
   式1
   式2
   :)
また、逆に偽の時に実行する unless もあります。

条件式

条件分岐に使う条件式では nil の場合に偽、 t など nil 以外の場合に真として判定されます。
nil 以外ですので、 数値の 0、 空文字などでも 真となります。
 (if nil "true" "false") ;; "false"
 (if t "true" "false")   ;; "true"
 (if 0 "true" "false")   ;; "true"
 (if "" "true" "false")  ;; "true"
空リスト '() が真か偽かは Lisp の処理系によって違うのですが、 elisp では空リストは nil です。
例えば、 変数に 空リスト '() を格納したとしても、その変数には nil が格納されることになります。
 (if '() "true" "false")  ;; "false"
 
 (setq foo '())
 foo ;; nil

一致判定

一致判定には eq を使います。
設定には基本的に eq を覚えておけば十分ですが、 似たような関数として、 equal というのもあり、こちらの方がゆるい一致判定といえます。

関数 機能
eq シンボル、整数値が一致。
equal 2 つの値が一致

 (eq 'foo 'foo)      ;; t
 (eq 1 1)            ;; t
 (eq "foo" "foo")    ;; nil
 (equal "foo" "foo") ;; t

論理式

elisp にも論理式があります。 設定に関して言えば、 not ぐらいを覚えておくぐらいでいいかと思います。

関数 意味 説明
not 否定 真、偽の反転
and 論理積 すべて真の時、真
or 論理和 どれか一つ真の時、真

 (not t)    ;; nil
 (not nil)  ;; t
 
 (and nil t) ;; nil
 (or  nil t) ;; t

設定でよく使う条件式

実際に設定でよく使う条件式を紹介します。

機能 切り替え
system-type OS の判定 OS
window-system Window システムの判定 ターミナルかどうか
file-exists-p ファイルの存在チェック ファイル、ライブラリーの有無
require, load ロードの成功、失敗
fboundp 関数が定義済みかどうかのチェック バージョン

OS

設定ファイルの共通化の基本としては OS で処理を変えることだと思います。
OS を表すシンボルが system-type の変数に格納されており、 これで OS を判断します。

格納されている主なシンボルを以下の表にあげます。 詳しくは C-h v system-type で変数の内容を確認するようにしてください。

シンボル タイプ
gnu/linux GNU/Linux
gnu/kfreebsd Free BSD
darwin Mac OS X
windows-nt Windows
cygwin Windows(Cygwin 版)


なお、 Windows 7 などでも Cygwin でビルドした Emacs でなければ、 windows-nt になります。
また、 シンボル名の '/' の文字はただの文字として扱われています。 elisp は前置法なので、 +, - などわりといろんな記号が変数(シンボル)名に使用できます。
 ;; Windows の場合、 IME の変換でエラーがでないようにする
 (when (eq system-type 'windows-nt)
   (global-set-key [M-kanji] 'ignore)
   (global-set-key [kanji] 'ignore)
   )

ターミナル

window-system の変数には実行時の Window システムが 'x', 'w32' などのシンボルで格納されています。 Window システムというのは、 Unix における X Window System など GUI を実現しているシステムです。 これで、 OS の判定ができなくもないですが、 system-type を使う方が普通です。
では、 window-system を何に使うかというと、ターミナルかどうか判定に使います。 ターミナルではキーコードなどの問題で切り替えが必要なことがあります。
Window システムが使われていないということで、ターミナルでは window-systemnil となっています。
 ;; ターミナルの場合(window-system が nil)、
 ;; <backspace> が C-h になるため、 C-h で一文字削除に設定
 (unless window-system
   (global-set-key "\C-h" 'delete-backward-char)             ;; 全般
   (define-key isearch-mode-map "\C-h" 'isearch-delete-char) ;; インクリメンタルサーチ用
   (add-hook 'c-mode-common-hook
            '(lambda ()
               (local-set-key "\C-h" 'c-electric-delete)))  ;; C 言語系モード
   )

ファイル、ライブラリーの有無

設定ファイルのロード中にエラーが発生すると途中で止まってしまいます。

file-exists-p の関数を使うと、ファイルの有無がチェックできるので、 ファイルがあるときだけロードするということができます。
 (setq custom-file "~/.emacs.d/custom_setttings.el")
 (if (file-exists-p custom-file)
     (load custom-file))
ただ、以前ファイルのロードのところで説明した load第 2 引数に t を指定すると、 「ロード対象のファイルが存在しない」という問題に関しては、エラーを発生しなくなります。
そのため、前述のコードは次のように書けます。
 (setq custom-file "~/.emacs.d/custom_setttings.el")
 (load custom-file t)
load と同様に require第 3 引数に t を指定 すると エラーが発生しなくなります。
これらはエラーは発生しなくても、ロードに成功すれば t 、 失敗すれば nil を返します。 これを利用して、 ロードし、成功した場合だけ実行する処理 が書けます。
 (when (require 'auto-complete-config nil t)
   (ac-config-default))

バージョン

共通して設定ファイルを使用する場合、すべての Emacs が同じバージョンとは限りません。 その対策として、バージョンの番号によって処理を切り替えてもよいのですが、 使いたい関数が定義されているかチェックしてから実行した方が確実です。
fboundp を使うと、関数が定義されているか をチェックすることができます。
 (if (fboundp 'normal-top-level-add-subdirs-to-load-path)
     (normal-top-level-add-subdirs-to-load-path))
以前、変数の解説で、一つのシンボルには 関数と変数が格納(結合)できる と説明しました。
fboundp は関数用ですが、 変数が定義されているかどうかをチェックする boundp という 関数もあります。 ただ、変数の場合は未定義の変数に値を設定したとしても、エラーではないので、 あまり設定では使用しません。



スポンサーサイト
 

Emacs におけるスクロールの挙動とその設定

Emacs ではデフォルトのままだとスクロールする時、ガクガクと動く感じがします。 その調整も含めて、今回は Emacs でのスクロールの挙動とその変更方法について説明します。

カーソル移動によるスクロール

なぜ、ガクガクとスクロールする感じになるかというと、 Emacs では画面端になって、スクロールする際に、 画面中央にカーソルがくるようにするためです。

emacs_scroll_step.png

これはこれで、なるべく画面の中央で編集ができるため、良い点もあります。 ただ、 Word など他のアプリと挙動が違うので、ちょっと気持ち悪く感じます。

ステップ量

中央に移動する挙動を変えるには scroll-conservatively の値を変更します。
この値のデフォルトは 0 であり、これを 1 にすると、 スクロールは 1 行となり、自然な感じになります。
(setq scroll-conservatively 1)
ちなみに、scroll-step の変数でも同じように変わりますが、 scroll-conservatively の方が新しく用意されたもので、大きな値などにも対応しており、よりよいです。

マージン

一行ずつスクロールするようになると、書き続けるうちに画面の下の方ばかりで編集することになることがあります。 見づらいですが、そういうものといえばそういうものですし、 C-l で中央にカーソルを持ってくることもできます。


ただ、 Emacs にはスクロールのマージンを指定する機能もあり、 これを設定すると画面の端に到達する前に画面をスクロールできるようになります。
(setq scroll-margin 5)
emacs_scroll_margin.png

1 画面のスクロール

1 画面のスクロールというのは C-vM-v によるページダウン、ページアップのことです。

重複行数

ページをスクロールする際、移動をわかりやすくするため、ちょっと重複するようになっています。 特にターミナルで動作させている時など、この重複は大事です。

この値は next-screen-context-lines で設定し、デフォルトは 2 です。
(setq next-screen-context-lines 5)
emacs_scroll_duple.png

カーソル位置の保持

scroll-preserve-screen-position の変数に t を設定すると ページスクロール時に画面上におけるカーソルの位置をなるべく変えないようにします。
(setq scroll-preserve-screen-position t)
emacs_scroll_conpos.png

他のアプリなどでは保持することが多いので、 どちらかというと設定しておいた方が自然かなという気がします。

マウスホイール

最近では Emacs でもデフォルトでマウスのホイールによるスクロールができるようになっています。
ただ、他のアプリではホイールでスクロールするとカーソルは移動せずにスクロールするものですが、 Emacs のスクロールはカーソルありきの移動です。 どちらが良い悪いとも言えませんが、同じような感じにすることはできません。 そのかわりといっては何ですが、 Emacs ではターミナルでさえ、ホイールスクロールが使えるようになっています。


ホイールスクロールに関しては、設定する必要はあまりありませんが、 Emacs では ShiftCtrl を押しながら、 ホイールした時のスクロール量の調整などもできるようになっています。
 ;; マウスホイールによるスクロール時の行数
 ;;   Shift 少なめ、 Ctrl 多めに移動
 (setq mouse-wheel-scroll-amount
       '(5                              ; 通常   (デフォルト 5)
        ((shift) . 1)                   ; Shift  (デフォルト 1)
        ((control) . 40)                ; Ctrl   (デフォルト nil = 無効)
        ))

設定用コード

init.el ファイルの記述用に設定をまとめてみました。
 ;; スクロールした際のカーソルの移動行数
 (setq scroll-conservatively 1)
 
 ;; スクロール開始のマージンの行数
 (setq scroll-margin 10)
 
 
 ;; 1 画面スクロール時に重複させる行数
 (setq next-screen-context-lines 10)
 
 ;; 1 画面スクロール時にカーソルの画面上の位置をなるべく変えない
 (setq scroll-preserve-screen-position t)
 
 
 ;; マウスホイールによるスクロール時の行数
 ;;   Shift 少なめ、 Ctrl 多めに移動
 (setq mouse-wheel-scroll-amount
       '(5                              ; 通常
        ((shift) . 1)                   ; Shift
        ((control) . 40)                ; Ctrl
        ))
 

Emacs で自動作成されるファイル(バックアップ、自動保存、ロック)の設定

Emacs ではファイルを編集すると、バックアップファイルという ~(チルダ)の付いたファイルができてしまい、これが結構うっとうしかったりします。
今回はこのバックアップファイルなどの Emacs が自動で作成するファイル、およびその設定について説明します。

Emacs が自動で作成するファイルの種類

Emacs がファイルの編集時に自動で作成するファイルはバックアップファイルを含めて大きく分けて 3 種類(細かく言うと 4 種類)あります。

種別 自動削除 用途
バックアップファイル foo.txt~ しない 編集前のファイルのバックアップ
自動保存ファイル #foo.txt# する 作業中のファイルの自動保存。 Emacs が不正終了した場合に使用
自動保存リストファイル .saves-xxx する 自動保存ファイルのリスト
ロックファイル .#foo.txt する 同時に修正しないためのロックに使用


これらのファイルは設定によって、作らないようにしたり、 作成先を変更したりできます。 お勧めは次のようにする感じです。
  • バックアップファイルは一カ所にまとめて作成
  • 自動保存ファイルは作成
  • 自動保存リスト、ロックファイルは作成しない
以降から 3 つのファイルについてそれぞれ解説していきます。

バックアップファイル (foo.txt~)

バックアップ(backup)ファイルはファイルをオープンする際、オープン時(編集前)のファイルをバックアップとして残します。 これは間違って修正や削除した時に使えます。

Emacs はかなり古くからあります。最近のソフトウェア開発ではバージョン管理システムを使うのが当たり前になってきているので、 いらないといえばいらないです。 うっとうしから作らないということにするというのも一つの手です。


作成しない場合には init.el に以下のように記述します。
 (setq make-backup-files t) 

ただ、チェックイン(コミット)はタイミングがあり、残しておきたいこともあります。 また、すべてのファイルをバージョン管理しているわけでもないです。
バックアップファイルはデフォルトだと編集対象と同じディレクトリーに作成します。 そのため、あちこちにできて、うっとうしくなります。 作成先を指定してまとめておくとそれほど邪魔ではありません。

作成先を変更する場合、 (対象ファイルの正規表現 . 作成先ディレクトリー) のペアー(コンスセル)を要素とするリスト(backup-directory-alist)で指定します。 デフォルトは nil(空) でリストの対象ファイルに引っかからない場合は元のファイルと同じところに作成します。
以下の例では すべて(".*")のファイルを対象に "~/.ehist" ディレクトリーに作成します。
 (setq backup-directory-alist '((".*" . "~/.ehist")))

移動して邪魔にならなくなったなら、一つ前だけでなく、もっとおいておきたいということもあります。番号をつけて複数前のまで残しておく なんてこともできます。
番号を付けて残す場合は、もちろん過去の全部残すってわけにはいかないので、範囲を指定します。
 ;; 番号付けによる複数保存
 (setq version-control     t)  ;; 実行の有無
 (setq kept-new-versions   5)  ;; 最新の保持数
 (setq kept-old-versions   1)  ;; 最古の保持数
 (setq delete-old-versions t)  ;; 範囲外を削除
kept-new-versions は過去の 5 個分残すという意味です。 kept-old-versions の方はちょっとわかりづらいですが、残し始めからの数です。 これが 1 であれば、最初(オリジナル)の分は残すってことになります。

自動保存

自動保存(auto-save)ファイルは編集中のファイルを保存したファイルで、 自動保存リスト(auto-save-list)ファイル はその自動保存しているファイルのリストを記述したファイルです。 これらは 正常に終了すると削除されますが、Emacs がエラーで落ちたりするとファイルが残ります。


Word などでも途中で落ちて、起動しなおすと編集中だったものが 「修復されたファイル」として出てきます。

Emacs の場合、異常終了した後、編集中だったファイルを開くと自動保存ファイルが残っていることを教えてくれます。 そこで M-x recover-this-file を実行して、自動保存された状態に戻すことができます。

ただ、それだと再度オープンした時にしかわかりません。 落ちた後、残っている自動保存ファイルを調べるには、自動保存リストファイルの中身を見ます。 recover-file というコマンドもあり、これはファイルを指定しての復帰です。
自動保存リストファイル自体は M-x recover-session でリストファイルのリストを表示することができます。

自動保存ファイル (#foo.txt#)

自動保存ファイルはバッファーを正常に閉じれば消えますし、復帰で助かることもあるので、 デフォルトのまま作るようにてしておいて問題ないと思います。


ちなみに自動保存をしない場合は次のようにします。
 (setq auto-save-default nil)

作成先を変更する場合、 (対象ファイルの正規表現 作成ファイルのパス t) のリストのリスト(auto-save-file-name-transforms)で指定します。
リストの 2 番目の要素はバックファイルのように作成先のディレクトリーでもいいですし、 正規表現のマッチング結果を使ったファイル名にすることもできます。 3 番目の t は名前の付け方を変えるオプションですが、 t で問題ないです。

リストの正規表現にマッチするものがない場合は元のファイルと同じところに出来ます。
デフォルトでは、一つだけリストに入っています。 これはリモートファイル( /ftp:xxx など)の場合に自動保存ファイルの作成先をローカルの一時ディレクトリー(temporary-file-directory)にするものです。
 '(("\\`/[^/]*:\\([^/]*/\\)*\\([^/]*\\)\\'" "一時ディレクトリー/\\2" t))
以下はすべてのファイル(".*")に対して "~/tmp" 以下に作成する例です。
 (setq auto-save-file-name-transforms   '((".*" "~/tmp/" t)))

この自動保存はデフォルトでは 30 秒たつか、 300 回入力操作を行うと実行されます。 ただ、デフォルトのままだとちょっと間隔が長いです。
自動保存の間隔を調整することもできます。
 ;; 保存の間隔
 (setq auto-save-timeout 10)     ;; 秒   (デフォルト : 30)
 (setq auto-save-interval 100)   ;; 打鍵 (デフォルト : 300)

自動保存リストファイル (~/.emacs.d/auto-save-list/.saves-xxxx)

こちらも正常に終了すれば消えるのですが、残っていたとしてもわざわざみて調べたりはあまりしないと思うので、 作る必要はないです。


作成しない場合は次のように記述します。
 (setq auto-save-list-file-prefix nil)
デフォルトではリストファイルは "~/.emacs.d/auto-save-list" のディレクトリーに作成されます。
以下は 作成先を変更する場合の例です。
 (setq auto-save-list-file-prefix "~/tmp/.saves-")
中止、作成先の変更ともに auto-save-list-file-prefix の変数を使います。
これはリストファイルの名前の前につける文字列で、これのパスの部分で作成先を変更し、 nil ならば作らないというようになります。 デフォルトは、この値が "~/.emacs.d/auto-save-list/.saves-" となっています。

ロックファイル (.#foo.txt)

ロック(lock)ファイル同じファイルを同時に編集しないためのロックとして作成されます。 こちらも正常に終了すると削除されます。
Emacs はもともと Unix 向けに作られたもので、 Unix は PC と違い、ネットを介して、あちこちからログインして使うことが想定されています。 ロックファイルはユーザーが同じファイルを編集していないかのチェックに使われます。

このロックファイルは ".#編集ファイル" の形式で編集ファイルと同じディレクトリーに作成されます。 実際はユーザー名やプロセス ID を組み合わせた長い名前のファイルを作成し、 そのシンボリックリンクとなっています。
自動で削除されるため作っておいても別にいいのですが、一人で使う PC などでは無駄なので、作成しないようにした方がいいでしょう。


作成しない場合は次のように記述します。 なお、作成先の変更はできないみたいです。
 (setq create-lockfiles nil)

特定のディレクトリーだけ作成しない

自動作成は行うけど、特定のディレクトリーにあるファイルに対しては作成したくないという時もあります。
例えば、 Google ドライブや DropBox のようなファイル共有サービスを使う場合、 その対象のディレクトリーは、サービス側で保存の度に履歴をとっているので、バックアップする必要がありません。


しかし、残念ながら Emacs では特定のディレクトリーだけ除くという機能は用意してくれてないみたいです。 というわけで自分で書いてみました。
 (defvar my-inhibit-auto-file-directory-list
   (list
    "~/DropBox"
    (concat (getenv "USERPROFILE") "/Google ドライブ")
    )
   "自動作成(バックアップ、自動保存、ロック)を禁止するディレクトリーのリスト")
   
 (add-hook 'find-file-hook
          '(lambda ()
             (when (listp my-inhibit-auto-file-directory-list)
               (let ((inhibit-ptn (concat "^\\(" (mapconcat '(lambda (str) (regexp-quote (expand-file-name str)))
                                                            my-inhibit-auto-file-directory-list "\\|") "\\)")))
                 (when (string-match inhibit-ptn buffer-file-name)
                   (setq backup-inhibited t) ;; バックアップ禁止
                   (auto-save-mode nil)      ;; 自動保存しない
                   (make-local-variable 'create-lockfiles) ;; ロックファイルを作成しない
                   (setq create-lockfiles nil)
                   )))))
my-inhibit-auto-file-directory-list のリストの要素にすると、そのディレクトリー以下(サブディレクトリーも含む)のファイルに対しては自動作成しないようになります。
自動作成ファイルは 3 つとも作らないようなっています。 変えたい場合は対象のところをコメントアウトしてください。


 
このページをシェア
アクセスカウンター
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

05月 | 2016年06月 | 07月
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 - -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。