Help us understand the problem. What is going on with this article?

Emacs教習所に行ってきた(チートシート付き)

Emacsで困ったらまずはとにかく

Ctrl+g (Ctrlキーを押しながらgキー)

を押してください。 Esc (escape キー) ではありません。ちなみに Esc を押したところで何も起きません。

Emacs キーバインドチートシート

約束

以降、

  • Ctrl を押しながらその他のキーを押す操作を C-
  • Alt を押しながらその他のキーを押す操作を M-

と表記します1。また、

  • C-x b という表記 => 「Ctrlを押しながらxを押した後、(一度キーボードから手を離して)bキーを押す」
  • C-x C-c という表記 => 「Ctrlを押しながらxを押した後、同じくCtrlを押しながらcキーを押す」
  • C-M-% という表記 => 「CtrlとAltとShiftを押しながら5キーを押す」

という点に注意してください。

Emacsはキーストロークが多いというのは有名ですよね...慣れましょう。

上記事項は重要ですので覚えておいてください。次以降に示すキーバインドはすぐに覚えられなくても徐々に慣れていけばいいです。2

基本的なキーバインド

ここではエディタにほしい最低限のキーバインドを載せます。

キーバインド 操作 コマンド名 実行される内容、補足
C-g エスケープ keyboard-quit Emacsのブレーキ。
C-x C-c 終了 save-buffers-kill-terminal Emacsを終了します。
C-x C-s 保存 save-buffer カレントバッファを保存します。
C-x C-f 開く find-file 指定したファイルを開きます。
C-/ アンドゥ undo あまり高性能ではないです。※1
C-@ 選択開始/終了 set-mark-command 選択状態が開始されます。※2
C-x h 全選択 mark-whole-buffer 全選択されます。
C-w 切り取り kill-region 選択範囲を切り取りします。
M-w コピー kill-ring-save 選択範囲をコピーします。
C-k 行切り取り kill-line 現在のカーソルより右を切り取ります。
C-y 貼り付け yank クリップボード※3の内容を貼り付けます。
C-s 検索 isearch-forward カーソル以降を検索します。※4
C-r 検索 isearch-backward C-sとは逆にカーソル以前を検索します。
C-M-s 正規表現検索 isearch-forward-regexp C-sの正規表現版です。
C-M-r 正規表現検索 isearch-backward-regexp C-rの正規表現版です。
M-% 置換 query-replace カーソル以降を対話置換します。
C-M-% 正規表現置換 query-replace-regexp M-%の正規表現版です。

※1 標準だとリドゥはありません。ないはず。 C-g に続けて C-/ でリドゥができますが、混乱しやすいです。 後述undo-tree を導入すると高度なアンドゥ/リドゥが行えます。
※2 押し続ける必要はないです。またEmacsでもShiftキーを使った選択は可能です。またEmacsでは選択領域のことを region (リージョン)と呼びます。
※3 Emacsではクリップボードのことを kill-ring といいます。
※4 連続で検索するには C-s を押し続けてください。

一般的なキーバインドを押してしまったときに何が起こるか

この他にもEmacsの重要なキーバインドはありますが、先に 普通のエディタでのショートカットキーを押したときに何が起きているか を紹介します。ちなみに普通のショートカットキーのことを Common User Access、cuaというそうです。cuaをEmacsに導入する方法もあるらしいですがここでは扱いません。

一般的なショートカット 期待する操作 あなたは何をしてしまったか Emacsでは
C-s 保存 検索を開始しています。落ち着いてC-gを押してください。 C-x C-s
C-a 全選択 カーソルが行頭に移動しただけです。逆の操作はC-e C-x h
選択状態でC-x 切り取り 次のキーの入力待ちとなっています。落ち着いてC-gを押してください。 選択状態でC-w
選択状態でC-c コピー 次のキーの入力待ちとなっています。落ち着いて(ry 選択状態でM-w
C-v 貼り付け scroll-up-commandによりページがスクロールされています。逆の操作はM-vです。 C-y
C-z アンドゥ suspend-frameコマンドを起動したためにEmacsが最小化しただけです。 C-/
C-y リドゥ クリップボード(kill-ring)内の内容が貼り付けられています。 C-g C-/ ( undo-tree の導入をおすすめします。)
C-f 検索及び置換 カーソルが前に一つ進んだだけです。逆の操作はC-b 検索:C-sで前 C-rで後ろ 置換:M-%

はじめに

ようやく「はじめに」を書きます。。。いえチートシートが最初の方が便利ですよね?そういう意図です。

これを書いている人は、大学の授業で初めてEmacsを触ったのですが、以前はEmacsが大嫌いでした。

  • ターミナルからファイルを指定して開くとなぜかウィンドウが2つに別れており下に意味不明な挨拶( 設定 で無効化できますしとりあえず編集したいファイルにカーソルがある状態で C-x 1 で消せます。)
  • 慣れているキーバインドを押しても何も起こらないかもしくは変になる(これの対策としてチートシートを載せました。)
  • Emacsを使って編集した人のファイルにてインデントでタブとスペースが混ざってた( 適切な設定 を行うことで解決できます。)
  • チルダがついたバックアップファイル群(意味がわかれば可愛いものです。またこちらも 設定 次第でだいぶ良くなります。)

などなど...VSCodeに慣れていた筆者にとっては、触れるのも意味不明な未開の地だったのです。

しかし大学が夏休みに入り、また僕自身が最近Lispを学び始めたこともあって、ちょっとやってみようかなと思い飛び込んだところ、「え、、ここまで改造できるの..?強よ...好き...」ってなりました。

逆に、いままでなんとなくVSCodeを使っていたことに気がついたんです。1ミリも理解していなくても使えるのがVSCodeの良さ、ということにも気が付きました。車で例えるならVSCodeがオートマでEmacsがマニュアル車です。

そういう思いもあって、とりあえず一通り使えるようになってきたので、大学の友達は自動車の教習所に行っているのを横目に、アウトプットを行うことにしました。

僕と同じように何かしらの理由でEmacsを使わなければならなくなった人、あるいは以前触れてみたけどわけがわからず挫折し、でもリトライしようって人の助けになれば幸いです。Emacs入門の良記事はQiita上でもすでにたくさん見られますが、「2019年版」として拙い記事ですが数えていただければと思います。

本記事は技術評論社 大竹智也 著 [改訂新版] Emacs 実践入門かなり 参考にしています。というか僕は主にこの本で勉強しました。予めご了承ください。

また紹介しているキーバインドは大半が自分の使ってるものだけになってます。さらにキーバインドに限らず、機能紹介も自分が気に入ったもののみになっています。「これも含めとけ」っていうのがあればコメントなり編集リクエストなりくださると幸いです。

環境

筆者の環境です3。Emacsのインストールについては、OSによっては少々面倒なため、一応後ほど別な記事にまとめる予定です。

  • Ubuntu 18.04.3 LTS (Bionic Beaver)
  • GNU Emacs 25.2.2

本記事では今後この環境を前提にします。

よっぽどドMじゃない方は Ubuntu等のLinux系OSでEmacsを使いましょう 。Macはまだ未検証でなんとも言えないのですが、少なくともWindowsでEmacsを使おうとすると数々の困難が訪れます。Windowsの環境しか用意できないという方なら、VirtualBox等で仮想環境を使用すると楽にEmacsをためせます。

本記事の終着点

VSCodeを今まで便利だと思って使ってたわけだから、VSCodeと同じぐらい便利じゃないとだめだよね?

というわけで、 EmacsをVSCode風にする のを本記事のサブタイトルかつ目的とします(自分ですらちょっと何言ってるかわからない。。ちなみにVSCode風と言っても筆者がほしい機能のみ導入してます。)

以降、解説の都合上、 かなり長々としたペーパーテスト対策の知識 が書き連ねられています。「キーバインド覚えるの面倒い!早くEmacsを使いたい!」って人は Emacs教習開始 まで飛んでください。

特にプログラマの方は実際に使いながら機能を覚えていくほうが早道だと思います。ただしペーパーテストの内容は前提として扱ってますので、わからなくなったら適宜戻ってもらえればと思います。

キーバインドの続き

基本的なキーバインド&プレフィックスキー

挨拶も終わったことですし先程の続きを...そのまえに Emacs教習所ペーパーテストで絶対出るプレフィックスキー&キーバインドたち の紹介です。

プレフィックスキーっていうのはもうすでにチートシートに出てきてたりする、「キーを押す前に押すキー」です。まず以下2つ!

C-x

Emacsの基本的な機能を呼び出すプレフィックス。ウィンドウ操作、保存、終了などが割り当てられていることがチートシートなどで確認できます。 eXecute の X と覚えましょう4

他のエディタで言う C-s による保存が、Emacsでは C-x C-s に割り当てられているのは、このプレフィックスがあるから、と考えると納得しやすいかと思います。

C-c

ユーザーが自分で定義するキーバインドにつけます。このキーバインドのおかげで安全に拡張が可能です。僕の場合、例えば C-c / に単体行コメントアウト機能を割り当てていたりします。

そして残り2つは少々特殊です。

M-x

打ち込んだあとに続けてコマンド名 (例えば save-bufferfind-file など) を打ち、Enterを押すとそのコマンドが実行されます。キーバインドを忘れたときや、そもそもキーバインドが設定されていないコマンドを実行するときに使えます。

以降、このキーバインドでコマンドを実行する場合について、本記事では M-x コマンド名 と表記します。つまりここまでをまとめると、 C-x C-fM-x find-file は同じ効果を期待した入力ということになります。

C-h

困ったときのヘルプ用コマンドです5。使用すると別ウィンドウで *Help* バッファを開きヘルプを表示してくれます。僕は主に以下の3つを使っています。 この記事を書くために使っています (現在進行系) 基本英語なのでちょっとむずかしいなって思ったら素直にググりましょう。

  • C-h k ( describe-key ): このコマンドに続けてキーバインドを入力するとそのキーバインドになんのコマンドが割り当てられているかを調べることができます。例えば C-h k C-h k と入力すると describe-key が割り当てられていることがわかります。
  • C-h f ( describe-function ): このコマンドに続けてコマンド名を入力すると、そのコマンド(Elispの関数です)の使い方や効果を調べることができます。
  • C-h w ( where-is ): C-h k とは逆に、コマンドがどのキーバインドに割り振られているかを調べられます。

2019/9/3 追記: uKLEinaさんからいただいた情報ですが、 C-h の代わりに F1 を使用することもできます。位置的にこちらのほうが覚えやすいかもしれません。

移動系キーバインド

Emacsを難しく感じさせることに定評がありそうな(偏見)移動系キーバインドの紹介です。

ちなみに Emacsでも十字キーは普通に使えます。 無理に覚える必要はないという意味です。僕は習得の過程で乗り換えましたけどね(ドヤ

キーバインド 操作 コマンド名 実行される内容、補足
C-f forward-char 右キーに等しいです。 forward の f
C-b backward-char 左キーに等しいです。 backward の b
C-n next-line 下キーに等しいです。 next の n
C-p previous-line 上キーに等しいです。 previous の p
C-a 行頭へ move-beginning-of-line あたま の a
C-e 行末へ move-end-of-line end の e
C-v ページダウン scroll-up-command PgDnキーに近いでしょうか?下に参ります。
M-v ページアップ scroll-down-command PgUpキーに近いですね。上に参ります。
M-< バッファの頭へ beginning-of-buffer 字面はわかりやすいですが3つのキー(Alt Shift ,)を同時に押さなければならないので結構大変です。
M-> バッファの最後へ end-of-buffer M-<同様に結構大変

Emacsの画面構成と関連機能

さっきから地味に「バッファ、バッファ」と牛のように言ってるのですが、残りのキーバインド紹介のためにはこのあたりについていい加減ちゃんと解説しなければ辛くなってきたのでここでさせていただきます。ついでにここでキーバインドも紹介し尽くすつもりです。

というわけで、Emacsの画面構成について説明していきます。bashで $ emacs text.md & などとしてファイルを開いた場合、何も設定していなければ次のように立ち上がるはずです。

emacs教習所1.png

フレーム

フレーム (frame) はEmacsのウィンドウそのものをいいます。呼び方が違うというだけで特に解説することはないです。フレームに関するキーバインドは僕は特に使ったことがありません。 C-z をアンドゥと間違って打ってしまうぐらいでしょうか?

ウィンドウ

ウィンドウ (window) はテキストエディタ部分と後述するモードライン部分をまとめた呼び方です。以下のキーバインドで増やしたり減らしたりできます。かなり便利ですので覚えましょう。以降、現在カーソルがあるウィンドウのことをカレントウィンドウと呼びます。

キーバインド 操作 コマンド名 実行される内容、補足
C-x 0 消去 delete-window カレントウィンドウを消します。※1
C-x 1 他を消去 delete-other-windows カレントウィンドウ 「以外」を消去します。
C-x 2 下に新しいウィンドウ split-window-below カレントウィンドウの下に分割
C-x 3 右に新しいウィンドウ split-window-right カレントウィンドウの右に分割
C-x o 別なウィンドウへカーソル移動 other-window ※2
C-x ^ カレントウィンドウを大きく enlarge-window 別なウィンドウがある場合、カレントウィンドウを大きくします。

※1 バッファは残ります。
※2 かなり頻繁に使うキーバインドなので、大竹さんの本に従い自分も C-t に割り当てなおしています。

バッファ

バッファ (buffer) は画像には名前しか描かれていませんが大切な概念です。バッファは他のエディタで言うところのタブ機能に近いものです。

エディタに限らずいろいろなもの(例えばヘルプとかエラーメッセージとか図とかその他Emacsのあらゆる機能)を表示するためにバッファは使われます。ウィンドウバッファ を表示する、というのが、Emacsの基本的な考え方です。バッファは常に見えているとは限りません。

基本的には、ファイル名がついているものがテキストエディタ(普通に開いた場合編集可能)のバッファで、 *scratch* のようにアスタリスクが両側についたものがEmacs側が用意するバッファです。自由に書き込めるもの、読み取り専用のもの、独自の操作が決まっているもの、など、操作はバッファによって異なる場合があります。

バッファ関連のキーバインドを以下にまとめます。一部チートシートの内容をもう一度まとめています。また、現在カーソルがあるバッファをカレントバッファと呼ぶこととします。(って書くのももう面倒なので以降カレントで現在のって意味であることは常識とします。。。今までも書く必要なかったかも...)

キーバインド 操作 コマンド名 実行される内容、補足
C-x b バッファ切り替え switch-to-buffer 指定したバッファに切り替えます。
C-x C-b バッファ一覧の取得と切り替え list-buffers 現在開いているバッファ一覧を表示※1
C-x k バッファの消去 kill-buffer バッファを消去します。逆にウィンドウは残ります。
C-x C-s 保存 save-buffer カレントバッファを保存します。
C-x s 全保存 save-some-buffers C-x C-c でも似たような挙動が得られます。終了はしません。
C-x C-f ファイルを開く find-file 指定したファイルをバッファとして開く
C-x C-q 読み取り専用にする read-only-mode カレントバッファを読み取り専用にします。※2
C-x C-r 読み取り専用で開く find-file-read-only 上記2つを組み合わせた感じです。

※1 *Buffer List* 内では 後述 のDiredと似たような操作ができ、選択したバッファが *Buffer List* と入れ替わる形でウィンドウに表示されます。
※2 編集したくないときに馬鹿よけで設定するといいでしょう。繰り返すことでオンオフを切り替えられます。

モードライン

モードライン (mode-line) にはカレントバッファについての様々な情報が載っています。

見るだけですから関連するキーバインドは特にないです(多分)。設定で表示内容を変更することもできます。 後述

画像のモードラインではこんな感じの情報が載ってます6

  • U : バッファがUnicodeエンコードであることを示しています。
  • : : 改行コードの種類に関する表示です。これはUnix系の LF です。
  • --- : ファイルに変更があるかを示しています。 -- が保存済み、 ** が未保存あり、 %% が読み取り専用です。
  • All : カレントバッファ全体のどのあたりがウィンドウ上で表示されているかを示しています。
  • L1 : カーソルが何行目に位置しているかを示しています。
  • (Fundamental) : モード が何であるかを示しています。Fundamentalモードは基本のモードであることを示しています。

モード というのは、簡単に言うとVSCodeでも右下に Markdown とか Python とか書いているあれです。

モードはバッファごとに設定されます。ファイルの書式自体で大きくEmacsの操作や挙動が変わる「メジャーモード」と、バッファの機能として付属的に使用できる「マイナーモード」があります。

後ほどもしばしば出てくるので、覚えておいていただけると幸いです。

ミニバッファ/エコーエリア

ミニバッファ (mini buffer)も基本的に見るだけか、コマンドに対する入力を与えるだけとなりますので関連するキーバインドは特にありません(多分)。が、かなり重要というかEmacsを使っていれば自然とまずはここに目が行くようになります。

例えばホームディレクトリにいるとして、 C-x C-f (もしくは M-x find-file でも良かったのですよね、覚えてますか?)と入力すると以下のように表示されます。

Find file: ~/

ここにファイル名を入力することで新しくファイルを開きます。もし存在していないファイルを開いた場合は次のように表示されているでしょう。

(New file)

これはEmacsからのメッセージです。こういった出力に関しては *Message* バッファからも確認することが可能です。

その他にも例えば C-x を押した後にキー入力待ちなら C-x- と出力されていたり、 C-s で検索を開始してしまった場合は I-search: と出力されているでしょう。

何が言いたいかというと 変な操作を開始したなと思ったら、ミニバッファを見れば一体自分が何をしてしまったかを確認できる ということです。僕もタイポとかよく起こして変なキーバインドを発動させてしまったりなどしょっちゅう起こしますが、そういうときはとりあえず冷静になってミニバッファを見れば良いのです。

ここのメッセージに親近感を覚え始めたら、個人的にはEmacsを使えるようになってきたと言っていいんじゃないかと思います。

ツールバー

ツールバー (tool bar) には各種コマンドに対するショートカットアイコンが並んでいるみたいですね。僕は使いません。以上。

その他のキーバインド

分類が難しかったけど紹介しておかないとまずそうなキーバインドを最後に載せておきます。

表にすると見づらかったのでリストで書きます。キーバインド、操作、コマンド名、実行される内容・補足、の順です。

  • C-q : そのまま入力する。 ( quoted-insert ) 例えば大抵のメジャーモードではTABキーを押すとインデントされてしまいますが、TAB文字を直に打ち込みたい場合があるかと思います。そういうときは C-q TAB と入力するとTAB文字を打てます。
  • C-u 数字 キーバインド : 繰り返し操作。( universal-argument ) そのあとの操作を繰り返すためのキーバインドです。 C-数字 でも起動されます。魔導物語でいうファファファイアーみたいな7。めったに使いませんが筆者は逆によく押し間違えて起動させてしまうキーバインドなので紹介しておきました。特定のコマンドはこの方法で起動すると面白い挙動をします。

Emacs教習開始

長らくおまたせしました、ここからようやく「Emacsを実際に使っていきます。」

エディタの演習って何すればいいんですかね?って普通ならなると思うのですが、なんとこの教習ではすでに題材が用意されています。それは。。ジャジャン!

init.el の作成

です!ドンドンパフパフ!

。。。はい、具体的にはEmacsをかなり使いやすくするために(記事の目標ではVSCodeレベル、でした)、設定を書いていきます。

プログラミングに慣れている人ならご存知かもしれませんが、 init というのは initialize 、すなわち初期設定のことを意味します。 Emacsの設定をするのにいじるのは主にこのファイルだけです

bashで言うところの .bashrc.bash_profile に近いです。。。なんでこいつらって2つあるんですかね?いっつもわけわからなくなるのですが、Emacsではそんなことを気にする必要はありません。VSCodeも設定ファイルを作れるそうですけど一体どこにあるのやら。。。Emacsではそんなことを気にする必要はないのです。

それにしても、「Emacsを使うためにEmacsを使う」ってメタ構造、なんかいいですね。こういうの僕は大好きです。

Elispについて(とても軽く)

ところで、 init.el.el という拡張子は何でしょうか?...ってもう見出しに答え書いてますけどね()

この init.el はEmacs Lisp、通称ElispというLisp方言で書いていきます。

「Lispってあの、関数型言語とかいう意味不明なプログラミング言語では...?」

と心配した方もいらっしゃるかもしれませんがご安心ください。 Elispはかなり手続き的に書かれることが多いです 8。つまりC言語やPython、Rubyみたいな感覚で書いていきます。また、ショートコーディングが基本なので競プロとかが好きな人はとっつきやすいんじゃないでしょうか?

どのみち、設定を書く前にまずはElispと触れ合う必要がありそうです。

Emacsの起動

ここまで読んで「なるほどぉ〜」ってなってくれた読者がどれぐらいいるかわかりませんが(0かな...?苦笑)、この記事はまだ重大なことをしていませんでした。それは「Emacsの起動」です。起動もしていないアプリケーションの話を長々と読んでくださったんですよね、本当にありがとうございます。

はじめに でも述べましたが、Ubuntuの場合で、GUIの場合についてのみ触れていきます。それ以外のOSの場合は申し訳ないですが起動方法を調べて立ち上げてください(現在別記事としてインストールガイドを執筆中です)。途中から同じになると思います。

Ubuntuなら、インストール後、Emacsのアイコンをクリックして立ち上げるか、ターミナルで次のように打ち込むことで起動できるはずです。それだけです。もったいぶってごめんなさい。もしターミナルで立ち上げる場合は、ホームディレクトリから起動していただけると以降記事と話が合います。

bash
$ emacs &

& はつけてもつけなくてもEmacsを使う分には変わりませんが、その後ターミナルで何かしたいならつけておいたほうが便利でしょう。

この方法(GUI)で立ち上げた場合は次のようになっていると思います。ちなみに -nw オプションを使用するとCUIで立ち上がります。

emacsScrn3.png

はい出ましたよくわからない挨拶ページ9。まぁVSCode等にもありますが。Elispが書けるようになったら init.el に設定を書いて消してやりましょうね。楽しみです。(せっかちな人のためにリンクおいておきます -> カスタマイズ開始:簡単なカスタマイズ )

とにかくまずはファイルを開きましょう。 C-x C-f を押した後に、 ~/ドキュメント/test.el となるようにファイル名を入力してEnterを押します。ドキュメントである必要もtestである必要もないですが、ホームディレクトリに直接ファイルを置くのも抵抗ありますよね。そこに配慮したパスです。一応言っておきますが、まだ設定ファイルを書く段階ではないので init.el とか紛らわしいファイル名はやめてください。

するとメジャーモードが (Emacs-Lisp) となったファイルが開くかと思います。「モード」はモードラインで確認できます。

モード というのは、モードライン の項でも説明しましたが、VSCodeで右下に書いている「Python」や「Markdown」といった表示と似たような機能です。

モードはバッファごとに設定され、ファイルの書式自体で大きくEmacsの操作や挙動が変わる「メジャーモード」と、バッファの機能として付属的に使用できる「マイナーモード」があります。

この後も出てきます。確認しておきましょう。

Elispを試してみる(1)

ではここを利用して軽くElispを体験しましょう。次の内容を打ち込んでください。

Elisp
(+ 10 10)

何やら足し算のようですね。20が返ってきそうです。打ち込みましたか?そしたら ) の後ろで C-x C-e を押してみてください。あ、このキーバインドの紹介がまだでした。

ElispはEmacs専用の言語なのでEmacsに最初からインタプリタ、コンパイラともに付属しているんです。便利ですね。 C-x C-ekmacro-end-and-call-macro コマンドを実行します。お察しの通り、カーソルの直前にあるElisp文 -- これをElispでは、というかLispではS式と呼びます。本記事でも以降S式と呼ぶことにします。 -- を評価して実行するコマンドです。

とにかく実行してみましょう。するとミニバッファに次のように表示されるはずです。

20 (#o24, #x14, ?\C-t)

返り値 20 ですね。後ろのは同値な表現です(8進数、16進数と、、最後のは詳しく知らないのですが文字としての結果のようです。)

つまり足し算を実行しました。予想どおりですね。

こんな感じで、普通のプログラミング言語では 関数名(引数) と実行するところを、Lispでは (関数名 引数) と書きます。色々試してみましょう。次はハローワールドでもしますか。

Elisp
(message "Hello, Elisp!")

こちらを実行すると "Hello, Elisp!" という表示がミニバッファに出ていると思います。いわゆる「printデバッグ」はElispではこの message 関数を使って行っていきます。

ここまで実行したあとは、ファイルの保存 C-x C-s 、Emacsの終了 C-x C-c 、Emacsの起動等を試してみていただけると、教習の内容としてはありがたいです。やってみましょう。

Elispを試してみる(2)

このまま test.el で実験を続けても良いのですが、ミニバッファでの表示は残らず(実際は *Message* バッファに残ってはいます)面倒くさいですから、もう少し便利な場所に移動しましょう。 C-x b を入力した後に、 *scratch* と入力してEnterを押してください。 *scratch**s あたりまで入力したところでTABキーを押せば補完されます。開くと次のような内容が書いているかと思います。

*scratch*
;; This buffer is for text that is not saved, and for Lisp evaluation.
;; To create a file, visit it with C-x C-f and enter text in its buffer.

「このバッファはセーブしないテキストとLisp評価のためのものです。ファイルを作りたいなら C-x C-f と入力してそのバッファにテキストを入力してください。」

なるほど、どうやらメモページのようですね。よくスクラッチパッドとかいうやつです。

このテキストの先頭についている ;; は何でしょうか? 外してみると赤から黒にテキストが変化するのが確認できるはずです。これはElispのコメントを表します。 ; がC言語で言う //、 bashやPythonで言う # に該当しています。 ; より ;;;;; とセミコロンの数が多いほど大きな塊に対するコメントだそうですが、コメントだとわかればそれで良いでしょう。

このページの4行目以降にElispを書いて実行してみることにしましょう。この記事をここまで読んでくれる読者の皆さんのプログラミング力を信じて、次はもう少し複雑なS式を書いてみましょう。シングルクォートが左側だけについた部分がありますが誤植ではありません。そのまま入力してください。

*scratch*
(let ((res ""))
  (dolist (num '(1 2 3 4 5) res)
    (setq res (format "%s %d" res (* num num)))))

書き終わったら一番最後の ) の後ろにカーソルを持ってきて...と、その前に待ってください。 C-x C-e では先程と同じです。せっかく *scratch* バッファにいるのですから、ここならではのキーバインドを使いましょう。 C-h k と入力した後に、 C-j を入力してください。

。。。。引っかかりましたね(ニヤリ)10。こちらはキーバインドヘルプを出すキーバインドです。紹介したいキーバインド C-j を利用してその他のキーバインドにも慣れてもらおうという狙いです。

ヘルプを表示させると、おそらく下に新しいウィンドウが *Help* バッファとともに出現して、

C-j runs the command eval-print-last-sexp (found in
lisp-interaction-mode-map), which is an interactive compiled Lisp
function in ‘elisp-mode.el’.

It is bound to C-j, <menu-bar> <lisp-interaction>
<eval-print-last-sexp>.

(eval-print-last-sexp &optional EVAL-LAST-SEXP-ARG-INTERNAL)

Evaluate sexp before point; print value into current buffer.

...

のように書かれていることかと思います。読んでみましょう。

C-jeval-print-last-sexp コマンドを走らせます。( lisp-interaction-mode-map にて見つけました。) このコマンドは elisp-mode.el にあるコンパイルされたインタラクティブな(対話的な、という意味です。)関数です。」

eval-print-last-sexp というのは、「最後のS式を評価しプリントする」と読めますね。 lisp-interaction-mode-map というのは何でしょうか?これはモードラインで確認できるモード Lisp Interaction と関連がありそうです。

実際、先程の test.el に戻って(バッファを切り替えるには C-x b を押した後にバッファ名 test.el を入れます。もしバッファに存在しないなら、 C-x C-f を押してファイル名を入力し、ファイルを開いてください。)同様にして C-h k を使って調べてみると electric-newline-and-maybe-indent という別なコマンドが、 global-map にて割り当てられていることがわかります。どうやらモードによって使えるキーバインドが違うようです。

ヘルプの続きを読みましょう。「このコマンドは C-j<menu-bar> <lisp-interaction><eval-print-last-sexp> コマンドと結びついています。」。確かにメニューバーを見てみると Lisp-Interaction という項目が増えていますね。そこからも実行できるそうです。

次の内容はElispで使用する場合の書式です。とりあえず飛ばしましょう。

で、その次に「カーソル(ポイント)より前にあるS式を評価し、返り値をカレントバッファに印字します」と書かれています。どうやら評価した値をカレントバッファ...つまり *scratch* に追記してくれるそうです!さっきよりは便利そうです。

もうこのヘルプには用がないです。表示が邪魔くさいのでウィンドウを消しましょう。もしカレントウィンドウが *scratch* 側ならば、 C-x 1 (小文字のエルではなく数字のいちキーです)を押すことでヘルプが書かれたウィンドウを消せます。逆に *Help* 側ならば、 C-x 0 でカレントウィンドウを消すことができます。

test.el でそのまま実行してたほうが変な解説を聞かずに済んだって?教習なのですから多少は我慢してください。もうヘルプ関連のキーバインドについては解説しませんので、他のヘルプコマンドを見たければ C-h の項目まで戻ってくださいね。

では改めてElispに触れていきます。次のS式を評価しようとしていたところでした。

*scratch*
(let ((res ""))
  (dolist (num '(1 2 3 4 5) res)
    (setq res (format "%s %d" res (* num num)))))

最後の ) の後ろで C-j を押してください。(もう騙しません)

するとその評価値 " 1 4 9 16 25" が次の行に挿入されます。どうやら各数字を2乗しただけのようです。

letdolistsetq は関数ではなくマクロと呼ばれるものですが、ちょっと書き方が独特になるだけで関数と似たものと捉えましょう。普通、関数のすべての引数は評価されるのですが、特殊形式という手法を用いて評価を止めるというのがマクロです11

let マクロは他の言語で言う変数宣言(と言うよりはアロケーションでしょうか?)とスコープの限定で、 dolist マクロは他の言語でいう foreach、そして setq は他の言語で言う代入です。

このコードにおいては、 let マクロでは res 変数を宣言して "" (空文字列) を束縛しています。(Lispでは代入を束縛とか呼んだりしてます。。違うのかな?用語の認識が曖昧なのでもしかしたらLisperに刺されるかもしれませんw) dolist の横の3つは特殊形式で、それぞれ (繰り返し変数 イテレータ dolistの返り値用変数) という順番になっています。イテレータというのは配列やリストのことで、この中身を繰り返し変数に代入しながら繰り返しが行われます。

イテレータ部分に当たる '(1 2 3 4 5) という表現は、Lispのリストです。ここで (1 2 3 4 5) としてしまうと 1 を関数とみなし評価されてしまいます。それを防ぐために ( の前にシングルクォート ' をつけています。

最後の (setq res (format "%s %d" res (* num num))) の部分について、 format 関数は第一引数をフォーマットとしてそれ以降の引数を入れた文字列を返します。C言語でいう sprintf 関数みたいなものです。そして、 setq マクロにて、 resformat 関数の返り値を束縛しています。

以上を踏まえると、次のRubyスクリプトとやっていることはほぼ同じであることがわかります12

Ruby
res = ""
for num in [1, 2, 3, 4, 5]
  res = sprintf("%s %d", res, num * num)
end

p res

Rubyが出てきたのでついでに話すと、Rubyの生みの親である、まつもとゆきひろさんはEmacsを愛用していたそうです。確かにLispを勉強していると、「これRubyにもある!」ってなる機能が多いのですが、つまりRubyはLispの影響を多大に受けていたわけですね13

話を戻します。要はこれが手続き的だと言った理由で、ところどころ関数型っぽくはできますが、特に関数型だとかを意識する必要はないのです。

Emacs向けにElispを書いてみる

「教習って言ってもEmacsのであってLispじゃないはずだ!もう飽きた!」って言ってブラウザのタブを閉じ始める人が出てきているかもしれません。。のでこのあたりでElispを実行することでEmacsに影響を与え、客足を戻してみようと思います。

次のS式を評価してみましょう。

Elisp
(set-cursor-color "#55c500")

カーソルの色がQiitaカラー #55c500 になったかと思います。もとに戻したければ、色を #000000 に設定して再評価するか、Emacsを再起動しましょう。

実際に init.el に書いていくのは主にこういった設定になっていきます。あまりここまで出てきたような数値の計算等はしません。これから何を記述していくのか、を理解していただけたなら幸いです。

いよいよinit.elを書いてみる

大変長らくおまたせしました。ようやく、 init.el を書いていきます。初運転です。

しかし、「 init.el を書くだけ」とうるさいぐらい言ってきたわけですが、一体どこに置くのでしょうか?

答えは ~/.emacs.d/init.el です。ホームディレクトリの下に隠しフォルダ .emacs.d を作り、その下に init.el を置きます。

解説を続ける前に一つ。実は設定ファイルとして認識されるファイルは4種類あったりします。(コンパイルも含めると6〜8種類となります。)

  1. ~/.emacs.el に記述する(または記述して ~/.emacs.elc にコンパイルする)
  2. ~/.emacs に記述する
  3. ~/.emacs.d/init.el に記述する(または ~/.emacs.d/init.elc )
  4. ~/.emacs.d/init に記述する

そして、これらの設定ファイルは、上にあるほど優先され他は無視されます。つまり例えば .emacs.el が存在した場合、 init.el をせっかく書いても読み込まれません。もしホームディレクトリに .emacs(.el) が存在するなら、内容を確認した上で何も記述されていなければ消しましょう。

また優先度はコンパイル済みのもの( .elc )のほうが高いです。もとい、先程ちらっと「コンパイラもある」みたいなことを言っていましたが、すべてのファイルについてコンパイルされたものがあればEmacsはそちらを優先して読み込み .el の方は無視します。これではしょっちゅう内容を書き換えることになる init.el とは相性が悪いので、僕はコンパイルしていません。

さらに言うと、 init.el ぐらいでしか今の所Elispを書いていないので実は僕自身コンパイル自体をそもそもしたことがなかったりします。。。というのは流石にどうかと思い今コンパイルして読み込み使用するというのをやってみましたが、面倒が増えただけでした。特に気にしなくていいです14

と、どうでもいいことを解説したところで、結局一つの疑問は残ります。なんで .emacs ではなく隠しフォルダを用意して init.el の方に書くのでしょうか?

答えは、大竹さんの本に従ったから...というのは冗談で(5割は本気)、 init.el を置く .emacs.d にてその他の設定ファイルや拡張機能を一括でまとめて管理するためです。

ここで細かいことによく気がつく勘のいいガキである読者の皆さんは、「は?設定ファイルは init.el だけって言ってたじゃん嘘つき」って思うかもしれません。が、どちらかと言うと設定ファイルを「分割することもできる」のです、、、すなわち、教習開始の冒頭に出てきた .bash_profile に書いてあるであろう source ~/.bashrc というコマンドじゃないですけど、「 init.el を中心に、分割したファイルを読み込む」という方法を取ります。 init.el がでかすぎるとそれはそれで大変でしょうから、この方法は必要といえます。

もし分割した設定ファイルがあり、それを読み込みたい場合は、

load書式
(load "拡張子を取り除いたElispファイル名")

init.el 側に書きましょう。ただし、分割した設定ファイルはbashでいう環境変数 $PATH に似た15 load-path リストに含まれたディレクトリにある必要があります。 load-path にディレクトリを追加する場合、以下のように書きます。

Elisp
(add-to-list 'load-path "ディレクトリ名")

分割設定ファイルのための設定

というわけで、設定ファイルを分割するかはまぁ init.el が大きくなってきたら考えるとして、分割ファイルを置く場所を load-path に含める設定をまず init.el に書きましょう。長いのでコピペでおkです。。。(Emacsでのペーストは C-y でしたね。ちなみにここでは使わないですが、 C-@ が選択開始[その後カーソル移動で選択]、 C-x h が全選択、 C-w が切り取りで M-w がコピーです。)

あと先に白状しておくとほぼ大竹さんの本に載っていた内容と同じです。p61からの引用です。以降大竹さんの本からまるまる取ってきたような部分については (実践入門p61) のように付記しておくこととします。

init.el
; (setq debug-on-error t)
(when (< emacs-major-version 23)
  (defvar user-emacs-directory "~/.emacs.d/"))

(defun namn/add-to-load-path (&rest paths)
  (let (path)
    (dolist (path paths paths)
      (let ((default-directory
              (expand-file-name (concat user-emacs-directory path))))
        (unless (file-exists-p default-directory)
          (make-directory default-directory))
        (add-to-list 'load-path default-directory)
        (if (fboundp 'normal-top-level-add-subdirs-to-load-path)
            (normal-top-level-add-subdirs-to-load-path))))))

(namn/add-to-load-path "elisp" "conf")

一応軽く解説しておきます。

Elisp
(setq debug-on-error t)

コメントアウトされていますが、一番最初の行のこれは、デバッグ用の記述です。 debug-on-errort (Elispでは真を示す値として使われます。) にすることで、 init.el を読み込んだときにエラーが発生した場合詳細なトレースバックを見ることができます。とりあえずオフでいいでしょう。

Elisp
(when (< emacs-major-version 23)
  (defvar user-emacs-directory "~/.emacs.d/"))

2行目以降のこれは、メジャーバージョンが23より小さい場合、Emacsが諸々に使用するディレクトリの場所を示す変数 user-emacs-directory を用意する処理です。こんな感じで環境に合わせて値を設定したりしなかったりという記述は init.el では割と頻繁に見られます。

Elisp
(defun namn/add-to-load-path (&rest paths)

この行からは namn/add-to-load-path 関数の定義をしています。 add-to-load-path はまぁそのままなのでいいとして、 namn ってなんですかね...?

...はい、自己紹介で名前を言っておりませんでした、わたくし、namn1125と申します。最近はnamniumを名乗っていたりします。つまり namn というのは僕の名前を表してました()

Elispの関数には名前空間みたいな考え方はないので、関数名に パッケージ名等のプレフィックス/ みたいな感じのものをつけたりして名前衝突を防いでるそうです。今回はこの部分でしか使用しない関数なので、一応、、、いらなかったかもしれませんが適当なプレフィックスをつけておきました。自分の名前の頭文字にでもしておいてください。

引数宣言をしている部分に書いてある &rest に引っかかった人がいるかもしれません。これは与えられた引数をすべて paths リストに入れてしまうという意味です。Pythonでいう可変長引数 *args みたいなものです。

ここで長々書いてもあれなのでだいぶ飛ばします(オイ

Elisp
(add-to-list 'load-path default-directory)

ここが例の部分ですね。下にある呼び出し部分と dolist の挙動を考えると、 default-directory として elispconf が入ってきそうです。意味合いとしては elisp は自作拡張機能、 conf は分割設定ファイルを入れるディレクトリで用意してます。(大竹さんがそうしてたから()、、、ちなみに両方共今の所僕は空っぽですハハハ)

その下の

Elisp
(if (fboundp 'normal-top-level-add-subdirs-to-load-path)
    (normal-top-level-add-subdirs-to-load-path))))))

ですが、これは default-directory が定義されている場合、そのサブディレクトリすべてを load-path リストに加えるという処理です。さらっと言いましたがかなり強力な関数ですね()

最後に今定義した関数を呼び出しています。

load関数以外でのElispファイルの読み込み

...今見返してみましたが説明がめちゃくちゃ長くなってますね...もう少しだけ辛抱してくださいすみません。ただここは読んでいただければわかるとは思いますが読み飛ばしていただいて結構です。

注意点として、 load はあくまでも分割ファイルに使ってください。というのも、loadによる読み込みは多重に行われる可能性があるため です。一度だけ読み込むにはフューチャーを利用した require 関数を使用します。

...さらに言うと require も使いません。 require を実行すると関数等がメモリに展開され重くなってしまいます。「呼び出されたらロードを行う」 autoload を代わりに使用します。。。

で、 autoload も直接書くことはあまりありません。なぜなら後述する package.el がいい感じで load-path への追加等含めて全てやってくれるからです。

この節の話は一応単語を出しておかないとまずそうだったのでおこなった取ってつけたようなものです。これが読まなくていいと言った所以です。せっかく読んでくれた人はゴメンネ

custom.el を作成させる

流れとしては飛ばしたかったのですが、設定ファイルに書き込んでほしい内容があるのでここに書きます。コピペでおk(Emacsでのペーストは C-y で、 C-@ が選択開始[その後カーソル移動で選択]、 C-x h が全選択、C-w が切り取りで M-w がコピーでしたね。何度も書いておきます。)

init.el
;; Emacs自体が書き込む設定先の変更
(setq custom-file (locate-user-emacs-file "custom.el"))
(unless (file-exists-p custom-file)
  (write-region "" nil custom-file))
(load custom-file)

Emacs自身(あるいは何らかの拡張機能)が設定を書きこみたいという時があるかもしれません。

デフォルトではそういった情報まで init.el に書き込まれてしまいます。これは管理するときに色々と面倒ですから、別ファイルに設定を書き込んでもらうようにしておきましょう。

Elisp
(setq custom-file (locate-user-emacs-file "custom.el"))

custom.el に絶対パスを付与して変数 custom-file に束縛しています。

Eilsp
(unless (file-exists-p custom-file)
  (write-region "" nil custom-file))

unlessif マクロに似たマクロの一つで、「条件節が偽なら以下の式を実行する」というものです。逆は when マクロです。

write-region にて、 custom-file のファイルに書き込みを行っています。

Elisp
(load custom-file)

また出てきましたね。このS式によって custom-file をロードしています。

Elispのパッケージ管理ツール

.emacs.d ディレクトリを設け、まとめて管理できるということによるメリットの一つに、「拡張機能を一括で管理できる」というものがありました。実はElispにもパッケージ管理ツールというものがあったりします。直接中身をいじることは多分ないのですが、パッケージ管理ツールが使用するディレクトリは .emacs.d 以下にあったほうが、直感的にはわかりやすくていいです。

「パッケージ」というのは、拡張設定やら拡張機能やら(もちろんElispです)のことを指します。具体例を挙げるほうがきっとこれを読んでいる方々にはわかりやすいと思うので挙げていくと、要は「 apt で取ってくるやつ」「 yum で取ってくるやつ」「 gem で取ってくるやつ」「 pip で取ってくるやつ」...etc みたいなやつです!

Elispでは、パッケージを管理しているアーカイブを ELPA (Elisp Package Archive)と呼び、Emacs標準で備わっているパッケージ管理ツールを package.el と呼びます。そのまんまですね。 package.el を使って ELPA から欲しいパッケージをインストールしてきます。

ELPAは有名なところで3箇所あるそうです。(実践入門p112)

package.el の欠点はバージョン管理機能がないとのことです。まぁ仕方がありません。

...でここまで解説しておいてアレなのですが、 本記事では package.el を直接は利用しません ...あくまでも「直接は」利用しないだけで、内部的には使用します。

代わりに本記事ではconao3さんが開発したleaf.elという拡張機能を利用してパッケージを管理します。

leaf.el の詳しい使用方法等については適宜conao3さんが書かれた記事を参考にしましょう。使う場面になったらもう一度リンクを載せます。

[正式リリース]leaf.elで雑然としたEmacs設定ファイル「init.el」をクリーンにする - Qiita

package.el を直接利用しない理由とGit+GitHubによる init.el の管理

package.el 関連の処理を直接書かない理由は、「設定ファイルは init.el 1枚に納める」という方針をさらに強固なものとするためです。

もし package.el を直接利用することにすると、次の2つの問題が生じます。

  1. init.el を見ても何のパッケージが必要でありそしてインストールされているのか、わからない
  2. 別な環境に丸々現環境を移したいとき、何かしらの方法でインストール済みパッケージを取得しなければならない

2については package-activated-list の値を吐き出させることで pip freeze と似たようなことができなくもないですが、明らかに手間です。

そこで1、2両方を解決する方法として、毎回 init.el を読み込むたびに「すでにパッケージがあればそのまま、なければ package.el で取ってきてもらう」という処理を init.el に書けばいいという方針になります。そのようなものとして有名な拡張機能に use-package.el というものがあるのですが、conao3さんがその構造を見直し開発したのが leaf.el です。

leaf という名が表す通り、階層構造を持って init.el にパッケージの設定を書けるようになります。 leaf.el を使用するために、conao3さんの記事にある通り次の設定を init.el に書きましょう。コピペでおk(Emacsでのペーストは C-y で、 C-@ が選択開始[その後カーソル移動で選択]、 C-x h が全選択、C-w が切り取りで M-w がコピーでしたね。何度も書い(ry )

init.el
(prog1 "prepare leaf"
  (prog1 "package"
    (custom-set-variables
     '(package-archives '(("org"   . "https://orgmode.org/elpa/")
                          ("melpa" . "https://melpa.org/packages/")
                          ("gnu"   . "https://elpa.gnu.org/packages/"))))
    (package-initialize))

  (prog1 "leaf"
    (unless (package-installed-p 'leaf)
      (unless (assoc 'leaf package-archive-contents)
        (package-refresh-contents))
      (condition-case err
          (package-install 'leaf)
        (error
         (package-refresh-contents)       ; renew local melpa cache if fail
         (package-install 'leaf))))

    (leaf leaf-keywords
      :ensure t
      :config (leaf-keywords-init)))

  (prog1 "optional packages for leaf-keywords"
    ;; optional packages if you want to use :hydra, :el-get,,,
    (leaf hydra :ensure t)
    (leaf el-get :ensure t
      :custom ((el-get-git-shallow-clone  . t)))))

どうしてここまで init.el に無理やり全てを押し込もうとするかというと、それは先ほどもちらっと触れましたが「別な環境に丸々現環境を移したい」からです。

そのための手段として、GitとGitHubを用います。Gitで init.el を管理すれば、環境を即座に別な端末に移せるだけではなく、コミットメッセージ等を利用して設定を入れた理由や履歴等を残すこともできます。

設定するべき .gitignore 等詳しくは別の記事にまとめておく予定です。(執筆中)

init.el の保存と確認、Diredについて

ここまで書いた段階で、設定が正しいかどうかの確認をしましょう。 C-x C-sinit.el を保存し、Emacsを C-x C-c で終了、その後再起動してみてください。うまく設定されていれば init.el が読み込まれ、ミニバッファが忙しく変わるのが確認できるはずです。(後から見てみたければ *Message* バッファに出力されているので、 C-x b でバッファを切り替えてみてみましょう。また init.el の読み込みが無限ループに陥っているようなときなどは例によって C-g で脱出できます。)

しかし、ちゃんと指定したディレクトリやファイルは作られたのでしょうか?確認してみましょう。

ここで今まで説明する場がなかった Dired の説明を一応しておきます。 C-x C-f でファイル名の代わりに ~/.emacs.d と打ち込んでみてください16。すると次のような表示が出てくるはずです。

.emacs.d
  /home/namnium/.emacs.d:
  合計 8
  drwx------ 1 namnium namnium 4096  9月  1 00:10 .
  drwxr-xr-x 1 namnium namnium 4096  9月  1 00:06 ..
  drwx------ 1 namnium namnium 4096  9月  1 00:10 auto-save-list
  drwxr-xr-x 1 namnium namnium 4096  9月  1 00:09 conf
  -rw-r--r-- 1 namnium namnium  760  9月  1 00:09 custom.el
  -rw-r--r-- 1 namnium namnium    0  9月  1 00:09 custom.el~
  drwxr-xr-x 1 namnium namnium 4096  9月  1 00:09 elisp
  drwxr-xr-x 1 namnium namnium 4096  9月  1 00:09 elpa
  -rw-r--r-- 1 namnium namnium 1693  9月  1 00:09 init.el

これは Dired の機能により作られた .emacs.d バッファです。指定したサブディレクトリ群はどうやらちゃんと作成されているようです。(作成されてなかったりする場合は .emacs.d のパーミッションがおかしかったりする可能性があります。 chmod 777 .emacs.d などをしてアクセス可能にしておきましょう。)

Dired ではディレクトリを見るだけではなくファイルを消したりなんかもできます。以下に操作を載せておきます。(実践入門p50)

  • Dired 基本操作
キー 説明
nかSPC 次の行へ
p 前の行へ
RETかf 開く
q 戻る、ウィンドウを閉じる
g 更新する※

※ 他のファイラ等で変更があった場合

  • Dired 編集操作
キー 説明
d 削除対象としてマーク
x 削除対象を削除
m マーク
*% 正規表現でマーク
[backspace] 1行上のマークを外す
u 現在行のマークを外す
*! 全てのマークを外す
+ ディレクトリを作成する
C-_ アンドゥ
D 削除※※
R mvコマンドと同等の挙動※※
C cpコマンドと同等の挙動※※

※※ マークがあればマークされたファイルを、なければ現在行のファイルを操作します。

elpa ディレクトリは leaf を導入したことで自動的に作成されたみたいです。覗いてみると依存したパッケージ群がインストールされているのを確認できるでしょう。以下は例です。

archives
el-get-20181006.225
gnupg
hydra-20190821.939
leaf-20190831.1244
leaf-keywords-20190816.1859
lv-20190821.947

カスタマイズ開始:簡単なカスタマイズ

何度も遠回りをして本当に申し訳ないです。というわけでここではいよいよ便利な機能を設定していきたいと思います。 以降はなるべくオムニバス形式というか、気に入った部分だけ読めばいいようにしておきます。

だいぶ前のほうでカーソルの色を変更して見せたと思いますが、手始めにそれとほぼ同レベルな細かい設定を書いてみましょう。。。といいつつ本格的な表示の変更に関して扱うのは後ほどです。

init.el
;; 現在位置列数表示
(column-number-mode t)

;; line番号表示
(global-linum-mode t)

;; よくわからない挨拶メッセージは非表示
(setq inhibit-startup-message t)

;; バックアップファイルの保存先の変更
(setq backup-directory-alist '((".*" . "~/.ehist")))

;; quitコマンドを用意...C-x C-cと違い強制終了させたい
(defalias 'quit 'kill-emacs)

(コマンド名 t) の引数 t はElispでの真偽値Trueにあたります。Elispでは nil 以外は全て真なのですが、真として汎用的に扱われるのが t です。

setq は以前説明したとおり変数を設定しています。 inhibit-startup-message (スタートアップメッセージの阻害)を真にすることで地味にあの忌々しかった挨拶バッファが出なくなっています。たいへんおまたせしました。ちなみに代わりとして、何もファイルを指定せずに立ち上げた場合は、あの便利なバッファ *scratch* が最初に出てくるようになります。やったね!

backup-directory-alist には、 (ファイルの拡張子 . ディレクトリ) というドット対のリストを設定することで、対象の拡張子になったファイルのバックアップファイル( ~ が後ろについたファイルの方です。)の保存先を任意に指定することができます。この設定をしておくとワークスペースをきれいに保てますね。

僕は面倒なので全てのファイルを ~/.ehist 以下にぶっこんでいます。...今書いていて気づいたのですが、少なくともUbuntuならホームディレクトリを ~ として指定してもちゃんと機能するんですね、記事内で指定方法に揺れがありますがご容赦ください。

その他、エイリアスを設定したりなどもできます。最後のは M-x quit って入力すればEmacsが閉じてくれるようにしたかったので個人的に入れました。

しつこいようですが、設定を反映するためには C-x C-s で保存してから C-x C-c でEmacsを閉じ、それからまたEmacsを起動します。

キーバインドを設定してみる

次に自分好みにキーバインドを作成してみましょう。キーバインドは define-key を使用することで作成できます。

define-key書式
(define-key キーマップ キーバインド コマンド名)

キーマップというのはモードに結びついたキーバインド集のことです。 **-mode-map として設定されます。全てのモードで有効にしたい場合は global-map に設定を行いましょう。ちなみに global-map に設定する場合は global-set-key を代わりに使用することもできます。

キーバインドは kbd 関数を用いて指定すると Ctrl c などを C-c のように記載できます。何を返すかは実際に実行してみるとわかるでしょう。

コマンド名には実行したいコマンドを渡します。 後述 しますが、自分が作成したコマンドも渡すことが可能です。

僕は global-map には以下のようなキーバインドを設定しています。それぞれのコマンドの効果等が知りたければ C-h f で確認してみましょう。

init.el
;; キーバインドの追加
(define-key global-map (kbd "C-t") 'other-window) ; ウィンドウ切り替え
(define-key global-map (kbd "C-x f") 'find-file) ; C-x C-f と同等。もともとはset-fill-columnだけど使わないので上書き
(define-key global-map (kbd "C-<up>") 'scroll-down-line)
(define-key global-map (kbd "C-<down>") 'scroll-up-line)
(define-key global-map (kbd "C-c k") 'kill-buffer-and-window)

いい加減覚えたとは思いますが、設定を反映するためには C-x C-s で保存してから C-x C-c でEmacsを閉じ、それからまたEmacsを起動します。

コマンドを作成してみる

「コマンド」「コマンド」って至るところに出てきていますが、自作コマンド、作ってみたくないですか?

もったいぶっていましたが、実はかなり簡単に作れます。VSCodeでどうやるのか逆に聞きたいぐらい簡単に作れます。

コマンドの書式
(defun (arg)
  (interactive)
  (...))

上に示すように、関数を定義するのとほぼ同じ感覚でコマンドは作れてしまいます。間で interactive 関数を実行するだけです。

僕が作成したコマンドを例に見ていきましょう。

init.el
;; 単体行コメントアウト用コマンド
(defun namn/comment-out-current-line ()
  "toggle comment out using comment-dwim"
  (interactive)
  (move-beginning-of-line 1)
  (set-mark-command nil)
  (move-end-of-line 1)
  (comment-dwim nil))
(global-set-key (kbd "C-c /") 'namn/comment-out-current-line)

interactive より前にある文字列はこのコマンドの説明です。これを書いておくと C-h f 等で調べた際、メッセージとして表示させることができます。Pythonでいう docstring と同じ機能ですね。

interactive 関数を実行後の動きを追ってみましょう。 C-h wC-h fC-h k の出番です!

  1. (move-beginning-of-line 1) : C-a の実行。先頭へ
  2. (set-mark-command nil) : C-@ の実行17。マークセット
  3. (move-end-of-line 1) : C-e の実行。末尾へ
  4. (comment-dwim nil) : M-; の実行。 region (選択範囲のことです) をコメントアウト

つまりこれは一連のキーバインドをまとめたVBAでいうマクロみたいなものです。

普段行う操作を簡略化したければ、そのとおり書けばいいということです。簡単でしょう?!

最後に global-map に自作コマンドを設定して準備完了です。ちなみにキーバインドを設定しなくとも、コマンドですから M-x namn/comment-out-current-line で実行できます。

例によってコマンドを有効化させるには普通は再起動が必要ですが、もしこのコマンドを試してみたいけどEmacsの再起動は面倒。。という人は今S式を評価させることで試してみましょう。

init.el でも *scratch* でも良いので上記コードを記述後、 (comment-dwim nil))(global-set-key (kbd "C-c /") 'namn/comment-out-current-line) それぞれの ) の後ろで C-x C-e を実行することでも、コマンドとキーバインドの有効化が可能です。気に入っていただけたなら namn の部分を自分の名前の頭文字とか my とか適当なものにして init.el に追加しておいてください。

その他の例も載せましょう。

init.el
;; chromeでプレビューするようにしたい
(when (executable-find "google-chrome")
  (defun namn/preview-by-chrome (file)
    (interactive "ffilename: ")
    (shell-command (format "google-chrome %s" file)))
  (global-set-key (kbd "C-c g") 'namn/preview-by-chrome))

executable-find 関数は、 exec-path リストに登録されているパス内に対象の実行可能ファイルがあるかどうかを調べます。 exec-path は起動時にOSの環境変数 $PATH を引き継ぎます。もしChromeがインストールされていれば、EmacsからChromeを起動することができるというコマンドです。

ここで一番注目してほしいのは、関数に引数が指定されていることと、 (interactive "ffilename: ") といった感じで、 interactive 関数に文字列を渡していることです。ffilenameは一見タイポに見えますが、そうではなく、1文字目のfで受け取る値の種類を指定しています。fはファイル名を受け取る指定子です。この指定子を先頭につけた文字列を interactive に指定するとミニバッファに filename: と表示され、補完機能が効いた入力を行えます。入力は file 変数に束縛され、その後の処理に使用されます。

その他の指定子は interactive について調べると確認することができます。関数を調べるヘルプのキーバインドは C-h f でしたね。

init.el
(defun replacer (reg str)
  "find with regexp and replace from current buffer"
  (interactive "sregexp: \nsto-string: ")
  (beginning-of-buffer)
  (query-replace-regexp reg str))
(define-key global-map (kbd "C-c r") 'replacer)

上記の例のように、文字列内に改行を含めることで複数の値を受け取るコマンドを作成することも可能です。

ちなみにこのコマンドを使えばバッファの先頭から検索が始まるので、置換について若干VSCodeと似た挙動を再現できます。

TAB狩り

ここまでで割といい感じにEmacsを使えるようになってきたのではないでしょうか?

しかしあと一つEmacsには不満点が残っていました。それは「TAB文字の混在」です。

検索でTAB文字の混在を調べることは実はこの記事の内容をよく読んでみるともうできたりします18。しかしVSCodeにはTAB文字を可視化する機能がありますね、羨ましい...というわけでEmacsにも導入してやりましょう!

ここで初めて leaf を使用します。 leaf については 前の方 を見てください。またconao3さんの記事を置いておきます。

[正式リリース]leaf.elで雑然としたEmacs設定ファイル「init.el」をクリーンにする - Qiita

次のコードをコピペしましょう。(Emacsでのペーストは C-y で、 C-@ が選択開始[その後カーソル移動で選択]、 C-x h が全選択、C-w が切り取りで M-w がコピー)

init.el
(leaf whitespace
  :ensure t
  :custom
  ((whitespace-style . '(face
                         trailing
                         tabs
                         ;; spaces
                         ;; empty
                         space-mark
                         tab-mark))
   (whitespace-display-mappings . '((tab-mark ?\t [?\u00BB ?\t] [?\\ ?\t])))
   (global-whitespace-mode . t)))

:ensure みたいなやつはシンボルといって、定数と文字列を組み合わせたような概念です。。って言うと語弊が生じますかね。。Rubyにあるシンボルと同じだと僕は思っています。

leaf において :ensure t が、「すでにインストールしていればそのまま、なければ package.el で取ってくる」ことを指示しています。

その次の :customsetq を楽に記述するために書いています。

ちなみに内容ですが、僕自身 whitespace-modeを使って、ファイルの保存時に行末のスペースや末尾の改行を削除する - Qiita の内容を丸々 leaf 用に加工しただけなのでよくわかってないですw

ともかくこれで TAB文字に色がつきTAB狩りの準備が整いました。適当なC言語ファイルでも作成し、Emacsの実際を見てみましょう。

次の内容に関しては、Emacsの自動インデント関係を確かめたいので、お手数ですが手打ちしてください。

C言語ファイル
#include <stdio.h>

int main (void) {
  for (int i = 0; i< 10; i++) {
    for (int j = 0; j < 10; j++) {
      for (int k = 0; k < 10; k++) {
        for (int l = 0; l < 10; l++) {
          printf("%d %d %d %d\n", i, j ,k ,l);
        }
      }
    }
  }
  return 0;
}

TAB狩り.png

すると上の画像のように途中でTAB文字が入り薄黄色でハイライトされているはずです。 インデント内にTABとスペースが混ざってるんです! 信じられませんよね。これはEmacsが嫌われる理由に地味に入ってきているのではないでしょうか。

ちなみにこれにはちゃんとした歴史的背景があるそうです。

はぁそうですか。。。いや知らないし!キモい!生理的に無理!って感じですよね。無効化しましょう。

init.el
(setq-default indent-tabs-mode nil)

本当はもっと前の方で紹介するべきだったのでしょうけどね、個人的にTAB文字には恨みがあるのでちゃんと解説しておきたかったのです。

個人的に恨みのあるTAB文字ですが、逆に必要なシーンも多々あります。その最たる例は makefile でしょう。 makefile という名前のファイルを開くと、 makefile-gmake-mode というメジャーモードになるのですが、ここでインデントを行うと、上記設定を行っていてもちゃんとTAB文字でインデントされていることが確認できます。

やっぱりTAB文字の混入検知が簡単にできるのはいいですよね。この設定は絶対やっておきましょう。

2019/9/3 追記: uKLEinaさんからいただいた情報ですが、 untabify コマンドで region のTAB文字をスペースに、 tabify コマンドで region の連続スペースをTAB文字に変えられます。便利そうです!

便利な拡張機能の導入

undo-tree

もう少しで教習も終わりです。がんばりましょう。Emacsへの不満点はほぼなくなったと言ってもいいはずだったのですが、まだアンドゥとリドゥに関しては難があります。次の leaf で導入しましょう。

init.el
(leaf undo-tree
  :ensure t
  :leaf-defer nil
  :bind (("M-/" . undo-tree-redo))
  :custom ((global-undo-tree-mode . t)))

:leaf-defer nil の部分についてですが、これは遅延評価を避けるために入れています。 :bind でキーバインドを、 :custom で変数を設定しています。

undo-tree を導入することで C-/ で複数のアンドゥ、 M-/ で複数のリドゥができるようになります。 undo-tree-mode はマイナーモードに分類されるので、有効になっている場合はモードラインから確認できるでしょう。

また、名前の通りgitのツリーのようにアンドゥ、リドゥ用のツリーで移動が可能になる、という機能がついているらしいですが、僕自身は使っていないので説明を省きます。

auto-complete

自動補完の拡張機能です。選択候補が出てくるようになります、、紹介しておきながらなんですが僕自身まだあまり使いこなせてなかったりします。

init.el
(leaf auto-complete
  :ensure t
  :leaf-defer nil
  :config
  (ac-config-default)
  :custom ((ac-use-menu-map . t)
           (ac-ignore-case . nil))
  :bind (:ac-mode-map
         ; ("M-TAB" . auto-complete))
         ("M-t" . auto-complete)))

auto-complete-mode もマイナーモードに分類されるので、有効になっている場合はモードラインから確認できるでしょう。

見た目の変更

筆者自身はあまりこだわったことはないのですが、Emacsの背景が白のままであることに嫌気がさしている人がいるかもしれません。

Emacsの見た目は theme を読み込んで変えたり、細かいものは face を変更することで変えられます。

zenburn-theme を導入するとAtomやVSCode風の黒い背景に白い文字となります。筆者の設定です。

init.el
(leaf zenburn-theme
  :ensure t
  :config (load-theme 'zenburn t))

また、細かい設定として対応するカッコのハイライトや、選択範囲の見た目の変更等は set-face-attribute を使用することで行なえます。

init.el
;; paren-mode : 対応するカッコを強調して表示
(setq show-paren-delay 0)
(show-paren-mode t)
(setq show-paren-style 'parenthesis)
;(set-face-background 'show-paren-match-face "gray") ; Emacs26で廃止
(set-face-attribute 'show-paren-match nil
      :background "gray"
      :underline 'unspecified)

;; regionの背景色文字色変更
(set-face-attribute 'region nil
                    :background "gray"
                    :foreground "black")

ついでにモードラインの見た目も変更しておきましょう。 smart-mode-line を導入するとなんかかっこよくなります(語彙力)

init.el
(leaf smart-mode-line
  :ensure t
  :custom ((sml/no-confirm-load-theme . t)
           (sml/theme . 'dark)
           (sml/shorten-directory . -1))
  :config
  (sml/setup))

multi-term

bashやzshなんかといったシェル、ターミナルとEmacsを連携させる拡張機能の紹介です。拡張機能の紹介はこれで終わりになります。本当は helm とか magit とかその他盛りだくさんの拡張機能を紹介するべきなのでしょうけども、 ターミナルと密な連携が取れればそもそもそんなに拡張機能を入れる必要はない のではないでしょうか。

ちなみに筆者がVSCodeが大好きであった最大の理由の一つは、「ターミナルを呼び出せること」です。WindowsなのにもかかわらずMSYS2を入れ、頑張ってbashを使用できるようにした後、VSCodeのターミナルをMSYS2にして今日まで頑張ってきた19のですが、 Emacsでもターミナルと連携できたらつまりVSCodeである必要性はなくなるわけです。 ということで早速紹介。

実はEmacsには標準でターミナルを呼び出す機能があったりします。 M-x termM-x shellM-x eshell というコマンドを使用すると、直ちにターミナルかターミナルモドキ20が立ち上がるのがわかると思います。

さらには、ターミナルを立ち上げずとも、キーバインド M-!shell-command というコマンドを実行できたりします。こちらを使用すると、 exec-path にパスが通ったコマンドならEmacsから実行することが可能です。

ということを紹介したところで、拡張機能 multi-term を紹介しておきます。この拡張機能を使用するとシェルを複数個立てることが可能になり、更にEmacsの操作感覚を残しつつ、ストレスレスなターミナル運用が可能となります。(うまく言語化できないのですが、、まぁ他のシェルを使っていろいろと実験してみるとわかります。)

筆者は次のように設定することで、VSCodeのターミナルと遜色のないターミナル環境を実現させました。

init.el
(leaf multi-term
  :ensure t
  :custom `((multi-term-program . ,(getenv "SHELL")))
  :preface
  (defun namn/open-shell-sub (new)
   (split-window-below)
   (enlarge-window 5)
   (other-window 1)
   (let ((term) (res))
     (if (or new (null (setq term (dolist (buf (buffer-list) res)
                                    (if (string-match "*terminal<[0-9]+>*" (buffer-name buf))
                                        (setq res buf))))))
         (multi-term)
       (switch-to-buffer term))))
  (defun namn/open-shell ()
    (interactive)
    (namn/open-shell-sub t))
  (defun namn/to-shell ()
    (interactive)
    (namn/open-shell-sub nil))
  :bind (("C-^"   . namn/to-shell)
         ("C-M-^" . namn/open-shell)
         (:term-raw-map
          ("C-t" . other-window))))

ちょっと長いですが、これはVSCodeに対抗するという筆者の執念です。諦めてください。

解説と使い方を説明していきます。気に食わない点は変えちゃいましょう。

init.el
:custom `((multi-term-program . ,(getenv "SHELL")))

(getenv "SHELL") で環境変数 $SHELL の値を取得し、これをターミナルのシェルとして設定しています。例えば筆者の環境では "/bin/bash/" が返ってきます。

:preface 以降では、キーバインド用にいくつかコマンドを作成しています。それぞれについて機能紹介したいと思います。

  • namn/open-shell-sub: 下2つのコマンドから呼び出される関数です。ウィンドウを分け、ターミナル用のウィンドウを小さくした後、ターミナル用のウィンドウに移ります。その後の挙動は呼び出すコマンドと状況によります。

  • namn/open-shell : 新しいターミナルを作成します。 C-M-^ にバインドしました。

  • namn/to-shell : 既存のターミナルがあればそちらに、なければ新規でターミナルを作成します。 C-^ にバインドしました。

C-^ にバインドした理由は、UbuntuのVSCodeでは C-~ (Ctrl+Shift+^) にターミナルがバインドされているので、それを真似した感じです。ただしターミナルの非表示はウィンドウの非表示 C-x 0 かカレントウィンドウ以外を消す C-x 1 で代用することにしました。

ここまでの設定を施せば無事VSCodeとほぼ同じ感じになります!やったね!

Emacs完成.png

init.el 全貌

コピペ用に本記事で紹介した init.el の内容全体を置いておきます。邪魔なので折りたたんでおきます。

init.el 全貌
init.el
; (setq debug-on-error t)
(when (< emacs-major-version 23)
  (defvar user-emacs-directory "~/.emacs.d/"))

(defun namn/add-to-load-path (&rest paths)
  (let (path)
    (dolist (path paths paths)
      (let ((default-directory
              (expand-file-name (concat user-emacs-directory path))))
        (unless (file-exists-p default-directory)
          (make-directory default-directory))
        (add-to-list 'load-path default-directory)
        (if (fboundp 'normal-top-level-add-subdirs-to-load-path)
            (normal-top-level-add-subdirs-to-load-path))))))

(namn/add-to-load-path "elisp" "conf")

;; Emacs自体が書き込む設定先の変更
(setq custom-file (locate-user-emacs-file "custom.el"))
(unless (file-exists-p custom-file)
  (write-region "" nil custom-file))
(load custom-file)

(prog1 "prepare leaf"
  (prog1 "package"
    (custom-set-variables
     '(package-archives '(("org"   . "http://orgmode.org/elpa/")
                          ("melpa" . "http://melpa.org/packages/")
                          ("gnu"   . "http://elpa.gnu.org/packages/"))))
    (package-initialize))

  (prog1 "leaf"
    (unless (package-installed-p 'leaf)
      (unless (assoc 'leaf package-archive-contents)
        (package-refresh-contents))
      (condition-case err
          (package-install 'leaf)
        (error
         (package-refresh-contents)       ; renew local melpa cache if fail
         (package-install 'leaf))))

    (leaf leaf-keywords
      :ensure t
      :config (leaf-keywords-init)))

  (prog1 "optional packages for leaf-keywords"
    ;; optional packages if you want to use :hydra, :el-get,,,
    (leaf hydra :ensure t)
    (leaf el-get :ensure t
      :custom ((el-get-git-shallow-clone  . t)))))

;; 現在位置列数表示
(column-number-mode t)

;; line番号表示
(global-linum-mode t)

;; よくわからない挨拶メッセージは非表示
(setq inhibit-startup-message t)

;; バックアップファイルの保存先の変更
(setq backup-directory-alist '((".*" . "~/.ehist")))

;; quitコマンドを用意...C-x C-cと違い強制終了させたい
(defalias 'quit 'kill-emacs)

;; キーバインドの追加
(define-key global-map (kbd "C-t") 'other-window) ; ウィンドウ切り替え
(define-key global-map (kbd "C-x f") 'find-file) ; C-x C-f と同等。もともとはset-fill-columnだけど使わないので上書き
(define-key global-map (kbd "C-<up>") 'scroll-down-line)
(define-key global-map (kbd "C-<down>") 'scroll-up-line)
(define-key global-map (kbd "C-c k") 'kill-buffer-and-window)

;; 単体行コメントアウト用コマンド
(defun namn/comment-out-current-line ()
  "toggle comment out using comment-dwim"
  (interactive)
  (move-beginning-of-line 1)
  (set-mark-command nil)
  (move-end-of-line 1)
  (comment-dwim nil))
(global-set-key (kbd "C-c /") 'namn/comment-out-current-line)

;; chromeでプレビューするようにしたい
(when (executable-find "google-chrome")
  (defun namn/preview-by-chrome (file)
    (interactive "ffilename: ")
    (shell-command (format "google-chrome %s" file)))
  (global-set-key (kbd "C-c g") 'namn/preview-by-chrome))

(defun replacer (reg str)
  "find with regexp and replace from current buffer"
  (interactive "sregexp: \nsto-string: ")
  (beginning-of-buffer)
  (query-replace-regexp reg str))
(define-key global-map (kbd "C-c r") 'replacer)

(leaf whitespace
  :ensure t
  :custom
  ((whitespace-style . '(face
                         trailing
                         tabs
                         ;; spaces
                         ;; empty
                         space-mark
                         tab-mark))
   (whitespace-display-mappings . '((tab-mark ?\t [?\u00BB ?\t] [?\\ ?\t]))))
  :config
  (global-whitespace-mode t))

(setq-default indent-tabs-mode nil)

(leaf undo-tree
  :ensure t
  :leaf-defer nil
  :bind (("M-/" . undo-tree-redo))
  :custom ((global-undo-tree-mode . t)))

(leaf auto-complete
  :ensure t
  :leaf-defer nil
  :config
  (ac-config-default)
  :custom ((ac-use-menu-map . t)
           (ac-ignore-case . nil))
  :bind (:ac-mode-map
         ; ("M-TAB" . auto-complete))
         ("M-t" . auto-complete)))

(leaf zenburn-theme
  :ensure t
  :config (load-theme 'zenburn t))

;; paren-mode : 対応するカッコを強調して表示
(setq show-paren-delay 0)
(show-paren-mode t)
(setq show-paren-style 'parenthesis)
;(set-face-background 'show-paren-match-face "gray")
(set-face-attribute 'show-paren-match nil
      :background "gray"
      :underline 'unspecified)

;; regionの背景色文字色変更
(set-face-attribute 'region nil
                    :background "gray"
                    :foreground "black")

(leaf smart-mode-line
  :ensure t
  :custom ((sml/no-confirm-load-theme . t)
           (sml/theme . 'dark)
           (sml/shorten-directory . -1))
  :config
  (sml/setup))

(leaf multi-term
  :ensure t
  :custom `((multi-term-program . ,(getenv "SHELL")))
  :preface
  (defun namn/open-shell-sub (new)
   (split-window-below)
   (enlarge-window 5)
   (other-window 1)
   (let ((term) (res))
     (if (or new (null (setq term (dolist (buf (buffer-list) res)
                                    (if (string-match "*terminal<[0-9]+>*" (buffer-name buf))
                                        (setq res buf))))))
         (multi-term)
       (switch-to-buffer term))))
  (defun namn/open-shell ()
    (interactive)
    (namn/open-shell-sub t))
  (defun namn/to-shell ()
    (interactive)
    (namn/open-shell-sub nil))
  :bind (("C-^"   . namn/to-shell)
         ("C-M-^" . namn/open-shell)
         (:term-raw-map
          ("C-t" . other-window))))

また、記事用に省略して書いたため、実際の筆者の init.el とは違ったりします。どうしても筆者の init.el を見たいという奇特な方のために、一応githubレポジトリのURLもおいておきます。

anotherhollow1125/dotfiles

最後に

ここまで読んでくださり本当にありがとう御座います。筆者としては無事に本記事の目標であった「EmacsをVSCodeレベルで使えるようにする」という目標を達成できたと思っていますが、どうでしょうか?

Emacsの魅力は記事内で伝えられたと思っていますが、事実として「入門してたかだか1ヶ月の人間が4万文字の技術記事を書きたくなる」ぐらいには魅力的なんだと思います。

よくEmacsはVimなどと一緒に宗教戦争のやり玉に挙げられ、非難されてしまっていますが、「Emacsを批判する人たちは init.el すら書いたことはないんじゃないだろうか?」と正直思います。(過去の自分だこれ)

僕は自分が使ったことのないツールを否定したくないです21。Vimについても勉強したいと常々思っていて、とりあえずEmacsに飽きて来たらVimもやってみようと思っています。つまり僕は宗教戦争に加わることはできませんし宗教戦争にそもそも興味がありません。

ただ一番言いたいのは「使ってからものを言え」です。この記事でEmacsに興味を持ってくれた人がいれば幸いです。

Emacsのコミュニティ

なんてポエムで終わるのもどうかと思うので最後にEmacsのコミュニティの紹介です。

emacs-jp.slack.com

僕はここで大変お世話になりました。特にconao3様には leaf の使い方を始めTwitter上からSlack上まで色々なところでお世話になり頭が上がりません。

初心者にも優しく答えてくれる良い場所です。teratailとかで質問するぐらいならここで尋ねたほうが早いでしょう。

また、 はじめに でも紹介しましたが、僕がEmacsの勉強に使用した本の紹介です。

技術評論社 大竹智也 著 [改訂新版] Emacs 実践入門

この本、実はまだ執筆時点で読破してません(笑)、しかし、この記事では紹介しきれなかったあんな機能やこんな拡張機能まで細かく載っています。本記事が役にたった!と思ってくださった皆さんなら絶対に買うべきです。買いましょう22!


はい、以上になります。Emacsド初心者が書いた記事ですので、ツッコミどころ満載でしょう。コメント、編集リクエスト、心よりお待ちしております。改めてここまで読んでいただきありがとうございました!


  1. C-M- はOSや各環境によって異なる可能性があります。 

  2. それに、紹介しておきながらいくつかのキーバインドは僕も利用していないです。 

  3. sudo apt install emacs でインストールした結果少し古くなってしまいました。Emacsはメジャーバージョンの違いがかなり設定ファイルに影響します。本記事より新しいバージョンのEmacsを使用する方は気をつけてください。 

  4. 実際の由来かどうかは知らないです。 

  5. C-h をバックスペースに宛てているという人をよく聞きますが、バックスペースはバックスペースで良くない?...まぁ人の好みだとは思います。 

  6. emacs のモードラインの見方 - ablogを参考にしました。 

  7. 魔導物語エアプですが。ぷよぷよはよくやります。 

  8. 記事を書く上で色々検索していたのですが、Elispのダメなところという記事を見つけ、そうなのだと知りました。知ったようなこと言ってますが僕自身はElisp歴半月です。ただ、実際にElispを書いてみて、手続き的だ、というのには同感しました。 

  9. Emacs公式のヘルプページなので初心者のEmacs講座モドキが大変失礼なことを言っている構図だったりします。 

  10. 引っかかってなかったら恥ずかしいやつ() 書くところがないのでこの注釈に書いておきますが、Elispを試す方法として、紹介した2つ以外に ielm コマンドを使用する方法があるそうです。 M-x ielm でElispのインタプリタが立ち上がります。Rubyのirbみたいに使えそうですね。ちなみに、 inf-ruby というEmacsからirbをつかうためのパッケージもあるそうです。 

  11. なんて言ってみましたが、実は僕自身まだ詳しくマクロを書いたことがないんです。。Elisp歴半月ですから勘弁してください() じゃあなんでEmacsの記事なんて書いてるんだって話ですけども 

  12. map を使ったほうがきれいに書けそうですね。 

  13. ここまで説明しておいてなんですが僕はどちらかと言うとPythonistaです。Rubyは普段書きません。 

  14. 一応コンパイルしてみたいという方のために手順の説明。1. .el ファイルを作成する 2. M-x byte-compile-file でコンパイルする 3. (add-to-list 'load-path コンパイル済みコードのあるディレクトリの絶対パス) を実行後 (load "ファイル名") ここでElisp内ではファイル名に拡張子 .el はつけません。。。面倒ですよね 

  15. M-! ( shell-command ) 等で呼び出せるコマンド群を探すための exec-path というリストもありますので、この説明は誤解を与えているかもしれません。あくまでもElispファイルをロードするときだけの話です。 

  16. ちなみに DiredC-x d でも呼び出せます。どうでもいいことですが、 Dired ってディレクトリエディタの略でいいのかな? 

  17. 拡張機能の作成者はユーザー用のマーク機能は使用してはいけない、みたいな決まりがあるそうですが、個人で使用する分にはなんの問題もないでしょう。 

  18. ちなみに正解は M-< ( M-x beginning-of-buffer ) → C-s → ( M-x isearch-forward ) → C-q TAB(タブ文字の入力) → C-s です。 

  19. 記事に含めるわけにもいかないのでここに書きますけど、 Windowsってほんとにクソですよね(怒) NTEmacsから頑張ってMSYS2との連携を試みたのですが折れました。。無理です。。Cygwinだともう少し楽に導入できるそうですが、いっそのことと思いWSLでの使用を試みています。ある意味「WindowsではEmacsは使えない」ということを証明してしまった気がしています。ちなみに、 NTEmacs @ ウィキ【8/28更新】 - アットウィキ の管理人さんすら匙を投げたのかWSL Emacsを使用しています。それと結局WSLとの連携でも多少なりとも面倒がつきまとって来ています。ドMの所業。。。 

  20. この表現はeshellを使用している人に失礼だろうか...でも事実あまり複雑なことはできないので仕方がないですよね() 

  21. Windowsはめちゃくちゃ使ってるから非難していいんです。って前友達に言ったら怒られたけど。 

  22. Qiitaはアフィサイトじゃないので安心してリンクを押してね、なんてね 

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away