Emacs入門

Emacsの基本と開発

チュートリアルの起動

Emacsを起動後、以下のコマンドでチュートリアルを開始できます:

C-h t

日本語版を起動するには:

C-h t japanese

これを練習しないと、例えばマニュアルでのキーバインドの特殊な記法の意味すら理解できません。上記のキーバインドの意味が分からない方は、プルダウンのHelpメニューから辿って下さい。時間はかかりません。すぐに終わります。

基本的な使い方

  1. 起動と終了:
    • 起動:ターミナルで emacs と入力
    • コマンドラインでの使用:emacs -nw と入力
    • 終了:C-x C-c
  2. 基本的なカーソル移動:
    • 前後左右:C-p, C-n, C-b, C-f
    • 行頭/行末:C-a, C-e
  3. ファイル操作:
    • 開く:C-x C-f
    • 保存:C-x C-s
  4. 編集:
    • コピー:M-w
    • カット:C-w
    • ペースト:C-y
  5. バッファ操作:
    • バッファ切り替え:C-x b
    • バッファ一覧:C-x C-b

Elispでの開発方法

  1. Elispファイルの作成:.el拡張子のファイルを作成
  2. 基本的な構文:
    (defun my-function (arg)
      "Documentation string."
      (interactive "p")
      (message "Hello, %s" arg))
  3. 評価:
    • 関数全体:カーソルを関数定義の末尾に置き C-x C-e
    • バッファ全体:M-x eval-buffer
  4. デバッグ:
    • デバッガの有効化:(setq debug-on-error t)
    • ブレークポイントの設定:(debug)
    • Flycheckによるsyntaxチェック
  5. パッケージ開発:
    • provideを使用してパッケージ名を定義
    • 依存関係はrequireで指定

極端な話、M-x load-file RETとC-h fとC-h vを知っていれば、ある程度までは試行錯誤で出来ます。まずは Programming in Emacs Lisp を読みましょう。

load-pathloadの違い

load-pathは、Emacsがライブラリを検索するディレクトリのリストです。新しいディレクトリを追加するには、add-to-list関数を使います。

;; ~/.emacs.d/init.el に記述
(add-to-list 'load-path "~/.emacs.d/site-lisp/")

これにより、~/.emacs.d/site-lisp/ディレクトリ内のファイルがEmacsの検索対象に追加されます。

load関数は指定したファイルをロードします。ファイルの拡張子(通常は.el)は省略可能です。

;; ~/.emacs.d/init.el に上記設定に続いて記述
;; ~/.emacs.d/site-lisp/my-settings.el
(load "my-settings")

これにより、~/.emacs.d/site-lisp/my-settings.elがロードされます。

require関数は指定したライブラリがロードされていなければロードします。provide関数とセットで使用されることが多いです。

flycheckを有効にしていると、elispファイルの末尾でprovideを書けと注意されるので、flycheck-copy-errors-as-killでコピーすると便利です。

;; ~/.emacs.d/site-lisp/my-settings.el に記述
(provide 'my-settings)
;;; my-settings.el ends here

;; ~/.emacs.d/init.el に記述
;; load-pathが通っていることが前提
(require 'my-settings)

設定ファイルを幾つも書いていて、まとめてloadする必要がある場合、EmacsWikiに投稿されているload-directoryを利用すると便利です。但し、load-directory.elは別のディレクトリに置いた方が良いかも知れません。以下の設定で、そのファイルを2度loadするからです。

;; load-pathが通っていることが前提
(load "load-directory")
;; 或いは (load "~/.emacs.d/site-lisp/load-directory")
;; provideが書かれていれば (require 'load-directory)
(load-directory "~/.emacs.d/site-lisp")

以下の関数はload-directoryを元にChatGPTが書いてくれました。loadではなく、requireを再帰的に実行します。

(defun require-directory (directory)
"Require recursively all `.el' files in DIRECTORY."
(dolist (element (directory-files-and-attributes directory nil nil nil))
(let* ((path (car element))
(fullpath (concat directory "/" path))
(isdir (car (cdr element)))
(ignore-dir (or (string= path ".") (string= path ".."))))
(cond
((and (eq isdir t) (not ignore-dir))
(require-directory fullpath))
((and (eq isdir nil) (string= (substring path -3) ".el"))
(require (intern (file-name-sans-extension path))))))))

以下は~/.emacs.d/init.elの設定です。

;; load-pathが通っていることが前提
;; load-directoryと同じファイル名と仮定
;; ~/.emacs.d/site-lisp/load-directory.el
(load "load-directory")
;; provideが書かれていれば (require 'load-directory)
(require-directory "~/.emacs.d/site-lisp")

MELPA (Milkypostman’s Emacs Lisp Package Archive)

MELPAは、Emacsの人気のあるパッケージリポジトリです。開発したパッケージを登録することも出来ます。プルリクエストとしてRecipeを提出する方法はREADMEを御覧ください。Pull requestsタブをクリックして書き方の参考にすると良いでしょう。

  1. MELPAの設定:
    (require 'package)
    (add-to-list 'package-archives
                 '("melpa" . "https://melpa.org/packages/") t)
    (package-initialize)
  1. パッケージのインストール:
    M-x package-install RET package-name RET
  2. パッケージの更新:
    M-x package-list-packages RET U x

keyword-searchプロジェクト

keyword-searchは、Emacsでブラウザスタイルのキーワード検索を可能にするパッケージです。

  1. インストール:
    M-x package-install RET keyword-search RET
  2. 基本的な使用方法:
    • M-x keyword-search RET
    • 検索エンジンを選択(TABで補完可能)
    • 検索クエリを入力
  3. カスタマイズ:
    • デフォルト検索エンジンの設定:
      M-x customize-variable RET keyword-search-default RET
    • 検索エンジンの追加:
      M-x customize-variable RET keyword-search-alist RET
  4. 拡張モード:
    • 追加の言語サービス:keyword-search-extra-mode
    • マニア向けウェブサイト:keyword-search-mad-mode

keyword-searchは、Emacsユーザーが効率的にウェブ検索を行えるようにする便利なツールです。MELPAを通じて簡単にインストールでき、カスタマイズも容易です。

browse-apropos-urlを元にMr. Jens Petersenが開発しました。私が書いたコードよりずっと素晴らしいです。私はプルリクエストを提出したことがきっかけで開発に参加しました。2024年現在、最後のコミットが6年前なので、動作しないURLや機能があるでしょうが、検索に役立つので是非試してください。

プログラミングの作業の大半は調べものなのですから。

Emacsでの定義元ジャンプと自動補完の設定

定義元ジャンプ

定義元ジャンプには、主に以下のパッケージが使用されます:

  1. xref: Emacsに標準搭載されているパッケージ
  2. dumb-jump: 外部ツールに依存しない汎用的なジャンプツール

xrefの設定

M-.xref-find-definitionsに、M-,xref-go-backに割り当てられています。xref-pop-marker-stackはEmacs 29.1で廃止されました。C-h k M-.でキーバインドを確認できます。設定されていなければ、以下のように設定し直すことが出来ます。

(global-set-key (kbd "M-.") 'xref-find-definitions)
(global-set-key (kbd "M-,") 'xref-go-back)

xrefを使う前に、TAGSファイルを作成する必要があります。以下のコマンドを使用してTAGSファイルを生成します。

Pythonでの例:

etags --help --lang=python
In Python code, 'def' or 'class' at the beginning of a line
generate a tag.

find . -type f -name "*.py" -exec etags -a {} +
find /usr/lib/python3/dist-packages/ -type f -name "*.py" -exec etags -a {} +

ctagsやetagsの拡張がaptでインストール出来ます。例えば:

sudo apt install universal-ctags
ls -l /usr/bin/etags
lrwxrwxrwx 1 root root 23  3月 16  2023 /usr/bin/etags -> /etc/alternatives/etags*
ls -l /etc/alternatives/etags
lrwxrwxrwx 1 root root 24  7月 16 22:10 /etc/alternatives/etags -> /usr/bin/ctags-universal*

dumb-jumpの設定

MELPAからインストール:

M-x package-install RET dumb-jump RET

設定。xrefの上書き:

(require 'dumb-jump)
(add-hook 'xref-backend-functions #'dumb-jump-xref-activate)

自動補完

自動補完には、company-modeを使用します。

MELPAからインストール:

M-x package-install RET company RET

設定:

(add-hook 'after-init-hook 'global-company-mode)

MELPAでインストールすべき便利なパッケージ

選択候補を表示した方が早いこともあります。例えば、yank-popを視覚化したcounsel-yank-popの方が使いやすいと多くの人が感じるでしょう。私も思います。

  1. ivy1: 補完フレームワーク
    M-x package-install RET ivy RET
  2. counsel2: ivyを使用した各種コマンドの強化版
    M-x package-install RET counsel RET
  3. swiper: ivyを使用した検索機能
    M-x package-install RET swiper RET
  4. projectile: プロジェクト管理ツール
    M-x package-install RET projectile RET
  5. magit: Gitクライアント
    M-x package-install RET magit RET
  6. flycheck: シンタックスチェッカー
    M-x package-install RET flycheck RET

flycheckの説明と設定

flycheckは、コードの文法チェックをリアルタイムで行うツールです。多くのプログラミング言語に対応しています。

お薦めの関数はflycheck-copy-errors-as-killです。その場のエラーをkill ringにコピーできるので、そのままyank出来ます。

因みに、この記事ではbuilt-inのFlymakeEglotの説明をしません。

設定:

(add-hook 'after-init-hook #'global-flycheck-mode)

global-flycheck-modeはあらゆる場面で起動するため、特定のメジャーモードでのみ起動する設定を推奨します。

(add-hook 'emacs-lisp-mode-hook 'flycheck-mode)
(add-hook 'python-mode-hook 'flycheck-mode)

Pythonの場合、Flycheck は python-flake8 または python-pylint を使用して Python をチェックし、どちらも使用できない場合は python-pycompile を使用します(fall back)。

Supported Languages
pip install pylint

lsp-modeの設定とlanguage serverのインストール

lsp-modeのインストールと設定

MELPAからインストール:

M-x package-install RET lsp-mode RET

設定例:

(require 'lsp-mode)
(add-hook 'python-mode-hook #'lsp)
(add-hook 'c-mode-hook #'lsp)
(add-hook 'javascript-mode-hook #'lsp)

(add-hook 'prog-mode-hook #'lsp) ;; あらゆるprogramming modesで起動

後者の設定はprog-mode3より派生するmajor modesに対応できるので便利です。

Language Serverのインストール方法

一つのプログラミング言語に対応する複数のlanguage serverがあります。

  • Python (pyright)
    npm install -g pyright
    which pyright
    /home/foo/.npm-global/bin/pyright
  • Python(jedi-language-server)
    pip install -U jedi-language-server
  • C/C++ (clangd)
    sudo apt install clangd
  • JavaScript (typescript-language-server)
    npm install -g typescript-language-server
  • Objective-C (clangd)
    sudo apt install clangd

lspの切り替え

C-u M-x lspつまり数引数を先に与えてlspを実行すると、language serverを切り替えることが出来ます。例えばPythonで複数のlanguage serverをインストールしている場合に便利です。

lsp-pyrightのインストールと設定はREADMEに書かれています。

使用しているvenvを指定するにはC-h lsp-pyright-venv-pathでcustomize this variableのリンクに入ります:

/home/foo/.venv3.12 ;; 初期値はnil
lsp-jediのインストールと設定もREADMEに書かれています。venvのpipでjedi-language-serverをインストールすると、lspが呼び出せませんので以下の作業が必要になります。また、Emacsで使用する前に毎回activateする必要があります。

まず、コマンドのパスを調べます:

source ~/.venv3.12/bin/activate # venvの例
which jedi-language-server

C-h v lsp-jedi-executable-commandでcustomize this variableのリンクに入ります:

jedi-language-server ;; 変更前の初期値
/home/foo/.venv3.12/bin/jedi-language-server ;; 変更例

Jediを使った他の補完方法

lsp-jediを使わずcompany-jediを使う方法もあります。

MELPAからインストール:

M-x package-install RET company-jedi RET

設定:

(defun my/python-mode-hook ()
  (add-to-list 'company-backends 'company-jedi))
(add-hook 'python-mode-hook 'my/python-mode-hook)

auto-completejedi.el(MELPAパッケージ名はjedi)を使う方法もあります。但し、virtualenvに依存します。aptやpipでのインストールが必要です。現在はvenvが主流ですので勘違いしないで下さい。両者は別物です。詳しくはマニュアルを参照して下さい。

以下の作業も必要になるでしょう:

M-x jedi:install-server RET
M-x jedi:reinstall-server RET ;; 2回目から 

~/.emacs.d/.python-environments/default/に仮想環境が作られます。

デバッグの設定と使い方

デバッグの設定と使い方についても簡単に説明します。

  • Emacs標準のgud-modeを使用する場合
    M-x gdb
    M-x pdb ;; Pythonの場合

    pdbの場合、ミニバッファに以下のように表示されるのでファイルを指定します:

    Run pdb (like this): python -m pdb myscript.py

    gdbやpdbのコマンド若しくはメニューから操作できます。コマンドについては脚注のリンク45を参照して下さい。

キー割り当て

キーボードに右Altが無いと、Emacsユーザーには不便です。以下のように設定すると、変換キーをEscのように使うことが出来ます。

;; 「無変換」キーで定義の先頭に移動
(global-set-key [muhenkan] 'beginning-of-defun)
;; MetaつまりAltを押しながら「無変換」で定義の末尾に移動
(global-set-key (kbd "M-<muhenkan>") 'end-of-defun)
;; 「変換」キーをEscのようにする
(global-set-key (kbd "<henkan>") esc-map)
;; 「カタカナひらがな」キーをM-xにする
(global-set-key [hiragana-katakana] 'execute-extended-command)

まとめ

設定したりパッケージを導入することで、Emacsの機能を大幅に拡張し、より効率的な開発環境を構築することができます。定義元ジャンプや自動補完、シンタックスチェックなどの機能は、コーディングの生産性を大きく向上させます。

Emacsのパッケージとは別に、aptやpipやnpmでインストールしてEmacsから呼び出すものがあるので混乱するかも知れませんが、そのソフトウェアの開発言語が何かを知っていれば、何故そのpackage managerでインストールするのか理解できるでしょう。

Citations:
[1] keyword-search.el
[2] Emacsの特徴や活用方法を徹底解説!
[3] Emacsチュートリアル 日本語訳
[4] emacs の使い方
[5] Emacs の簡単な使い方
[6] #17 “手遅れ”を防ぐ Emacsのセーフガードシステム (Software Design 2015年9月号掲載記事) Emacs undo redo 使い方
[7] 突然だがEmacs を始めよう
[8] Programming in Emacs Lisp
[9] EmacsでFlycheckのウィンドウをトグルできるようにした
[10] ctagとetagについて #8
[11] exuberant-ctagsとuniversal-ctagsの違い
[12] Flycheck を Python で使うためにしたことメモ
[13] Flycheck — Syntax checking for GNU Emacs

  1. helm を背に ivy の門を叩く ↩︎
  2. Vimmer から見た Emacs ファジーファインダーの歴史について ↩︎
  3. Prog Mode ↩︎
  4. 3 デバッグのお供: ”gdb のススメ” ↩︎
  5. pdb — Python デバッガ ↩︎