GNU Emacs

開発ツール

GNU Emacs

概要

GNU EmacsはRichard Stallman氏によって開発された高度に拡張可能なテキストエディタです。「オペレーティングシステム」とも呼ばれる豊富な機能群と、Emacs Lispによる無限の拡張性で、多くのプログラマーに愛用されています。

詳細

Emacsは1976年にRichard Stallman氏によって開発が開始され、1985年にGNU Emacsとしてリリースされました。単なるテキストエディタを超えて、メール、ウェブブラウジング、カレンダー、ゲーム、プログラミング環境など、あらゆる機能を統合したプラットフォームとして機能します。

Emacsの最大の特徴は、Emacs Lispによる完全なプログラマビリティです。エディタのほぼ全ての機能がEmacs Lispで記述されており、ユーザーは自由に機能を追加・変更できます。org-mode(情報整理)、Magit(Git クライアント)、mu4e(メールクライアント)、evil-mode(Vim エミュレーション)など、強力なパッケージが豊富に存在します。

セルフドキュメンティング設計により、エディタ内で全ての機能のドキュメントにアクセスでき、関数定義の参照や動的な機能探索が可能です。バッファベースのウィンドウシステム、強力なキーバインドシステム、正規表現による検索・置換、多様なプログラミング言語サポートなど、高度なテキスト編集機能を提供します。

メリット・デメリット

メリット

  • 無限の拡張性: Emacs Lispによる完全なカスタマイズ可能性
  • 統合プラットフォーム: エディタ、メール、ブラウザ、PIM機能の統合
  • セルフドキュメント: エディタ内での完全なドキュメント参照
  • 強力なパッケージ: Org-mode、Magit、mu4eなど高機能パッケージ
  • プログラマブル: あらゆる操作をプログラムで自動化可能
  • 長い歴史: 40年以上の開発で培われた安定性と成熟度
  • 独立性: 他のツールに依存しない完結した環境

デメリット

  • 急峻な学習曲線: 独特な操作体系の習得に長期間必要
  • 重いリソース消費: 多機能ゆえにメモリと CPU を大量消費
  • ターミナルでの制限: GUI版と比較してターミナル版は機能制限
  • 初期設定の複雑さ: 基本的な使用でも相当な設定作業が必要
  • UI の古さ: 現代的なエディタと比較してインターフェースが古い
  • プラグイン管理: パッケージ間の競合や互換性問題
  • 起動時間: 大量の設定読み込みにより起動が遅い

主要リンク

書き方の例

基本設定(init.el)

;; ~/.emacs.d/init.el - 基本設定
;; パッケージシステム設定
(require 'package)
(setq package-enable-at-startup nil)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/"))
(add-to-list 'package-archives '("gnu" . "https://elpa.gnu.org/packages/"))
(package-initialize)

;; use-packageのインストール
(unless (package-installed-p 'use-package)
  (package-refresh-contents)
  (package-install 'use-package))

(eval-when-compile
  (require 'use-package))

;; 基本設定
(setq inhibit-startup-message t)       ; スタートアップメッセージを非表示
(setq inhibit-scratch-message nil)     ; scratchバッファメッセージ
(setq initial-scratch-message "")      ; 初期メッセージを空に

;; UI設定
(menu-bar-mode -1)                     ; メニューバーを非表示
(tool-bar-mode -1)                     ; ツールバーを非表示
(scroll-bar-mode -1)                   ; スクロールバーを非表示
(global-display-line-numbers-mode 1)  ; 行番号表示
(column-number-mode 1)                 ; カラム番号表示
(show-paren-mode 1)                    ; 対応する括弧をハイライト
(global-hl-line-mode 1)                ; カーソル行をハイライト

;; インデント設定
(setq-default indent-tabs-mode nil)    ; タブをスペースに変換
(setq-default tab-width 4)             ; タブ幅
(setq indent-line-function 'insert-tab) ; インデント関数

;; バックアップとオートセーブ
(setq backup-directory-alist `((".*" . ,temporary-file-directory)))
(setq auto-save-file-name-transforms `((".*" ,temporary-file-directory t)))
(setq backup-by-copying t)
(setq delete-old-versions t)
(setq kept-new-versions 6)
(setq kept-old-versions 2)
(setq version-control t)

;; エンコーディング
(set-language-environment "Japanese")
(prefer-coding-system 'utf-8)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)

;; キーバインド
(global-set-key (kbd "C-h") 'delete-backward-char)
(global-set-key (kbd "M-?") 'help-command)
(global-set-key (kbd "C-c l") 'toggle-truncate-lines)
(global-set-key (kbd "C-c r") 'replace-string)
(global-set-key (kbd "C-c R") 'replace-regexp)
(global-set-key (kbd "C-x C-b") 'ibuffer)

;; フォント設定
(when (eq system-type 'darwin)
  (set-face-attribute 'default nil :family "Monaco" :height 140))
(when (eq system-type 'gnu/linux)
  (set-face-attribute 'default nil :family "DejaVu Sans Mono" :height 110))
(when (eq system-type 'windows-nt)
  (set-face-attribute 'default nil :family "Consolas" :height 110))

パッケージ設定(use-package)

;; テーマ設定
(use-package doom-themes
  :ensure t
  :config
  (setq doom-themes-enable-bold t
        doom-themes-enable-italic t)
  (load-theme 'doom-one t)
  (doom-themes-visual-bell-config)
  (doom-themes-org-config))

;; 補完フレームワーク
(use-package ivy
  :ensure t
  :init
  (ivy-mode 1)
  :config
  (setq ivy-use-virtual-buffers t)
  (setq enable-recursive-minibuffers t)
  (global-set-key (kbd "C-s") 'swiper)
  (global-set-key (kbd "M-x") 'counsel-M-x)
  (global-set-key (kbd "C-x C-f") 'counsel-find-file))

(use-package counsel
  :ensure t
  :after ivy)

(use-package swiper
  :ensure t
  :after ivy)

;; プロジェクト管理
(use-package projectile
  :ensure t
  :init
  (projectile-mode +1)
  :bind (:map projectile-mode-map
              ("s-p" . projectile-command-map)
              ("C-c p" . projectile-command-map)))

;; Git統合
(use-package magit
  :ensure t
  :bind (("C-x g" . magit-status)
         ("C-x M-g" . magit-dispatch)))

;; 会社補完
(use-package company
  :ensure t
  :init
  (global-company-mode)
  :config
  (setq company-idle-delay 0.2)
  (setq company-minimum-prefix-length 1)
  (setq company-selection-wrap-around t))

;; ファイルツリー
(use-package neotree
  :ensure t
  :config
  (global-set-key [f8] 'neotree-toggle)
  (setq neo-theme 'arrow))

;; Org-mode拡張
(use-package org
  :ensure t
  :config
  (setq org-directory "~/org/")
  (setq org-default-notes-file (concat org-directory "notes.org"))
  (setq org-agenda-files (list org-directory))
  (global-set-key (kbd "C-c a") 'org-agenda)
  (global-set-key (kbd "C-c c") 'org-capture)
  (global-set-key (kbd "C-c l") 'org-store-link))

;; Evil mode (Vim エミュレーション)
(use-package evil
  :ensure t
  :init
  (setq evil-want-integration t)
  (setq evil-want-keybinding nil)
  :config
  (evil-mode 1))

(use-package evil-collection
  :after evil
  :ensure t
  :config
  (evil-collection-init))

;; LSP
(use-package lsp-mode
  :ensure t
  :init
  (setq lsp-keymap-prefix "C-c l")
  :hook ((python-mode . lsp)
         (javascript-mode . lsp)
         (lsp-mode . lsp-enable-which-key-integration))
  :commands lsp)

(use-package lsp-ui
  :ensure t
  :commands lsp-ui-mode)

(use-package lsp-ivy
  :ensure t
  :commands lsp-ivy-workspace-symbol)

Org-mode設定例

;; Org-mode詳細設定
(use-package org
  :ensure t
  :config
  ;; 基本設定
  (setq org-directory "~/org/")
  (setq org-default-notes-file (concat org-directory "inbox.org"))
  (setq org-agenda-files (list (concat org-directory "gtd.org")
                               (concat org-directory "projects.org")
                               (concat org-directory "calendar.org")))
  
  ;; TODO キーワード
  (setq org-todo-keywords
        '((sequence "TODO(t)" "NEXT(n)" "WAITING(w)" "|" "DONE(d)" "CANCELLED(c)")))
  
  ;; タグ
  (setq org-tag-alist '(("@work" . ?w)
                        ("@home" . ?h)
                        ("@computer" . ?c)
                        ("@phone" . ?p)
                        ("@errands" . ?e)))
  
  ;; キャプチャテンプレート
  (setq org-capture-templates
        '(("t" "Todo" entry (file+headline org-default-notes-file "Tasks")
           "* TODO %?\n  %i\n  %a")
          ("j" "Journal" entry (file+datetree "~/org/journal.org")
           "* %?\nEntered on %U\n  %i\n  %a")
          ("m" "Meeting" entry (file+headline "~/org/meetings.org" "Meetings")
           "* %? :meeting:\n  %U\n  %i")
          ("n" "Note" entry (file+headline "~/org/notes.org" "Notes")
           "* %?\n  %U\n  %i")))
  
  ;; アジェンダ設定
  (setq org-agenda-custom-commands
        '(("d" "Daily agenda and all TODOs"
           ((agenda "" ((org-agenda-span 1)))
            (alltodo "" ((org-agenda-overriding-header "All TODOs")))))))
  
  ;; バベル設定(コード実行)
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((python . t)
     (shell . t)
     (emacs-lisp . t)
     (javascript . t)))
  
  ;; キーバインド
  (global-set-key (kbd "C-c a") 'org-agenda)
  (global-set-key (kbd "C-c c") 'org-capture)
  (global-set-key (kbd "C-c l") 'org-store-link))

カスタム関数例

;; カスタム関数定義

;; 現在行を複製する関数
(defun duplicate-line ()
  "Duplicate the current line."
  (interactive)
  (let ((line (buffer-substring-no-properties
               (line-beginning-position)
               (line-end-position))))
    (end-of-line)
    (newline)
    (insert line)))

;; 末尾の空白を削除する関数
(defun delete-trailing-whitespace-and-save ()
  "Delete trailing whitespace and save the file."
  (interactive)
  (delete-trailing-whitespace)
  (save-buffer))

;; ウィンドウ分割
(defun split-window-below-and-switch ()
  "Split window below and switch to it."
  (interactive)
  (split-window-below)
  (balance-windows)
  (other-window 1))

(defun split-window-right-and-switch ()
  "Split window right and switch to it."
  (interactive)
  (split-window-right)
  (balance-windows)
  (other-window 1))

;; 設定ファイルを開く関数
(defun open-init-file ()
  "Open the init file."
  (interactive)
  (find-file user-init-file))

;; 現在のファイルをリロードする関数
(defun reload-init-file ()
  "Reload the init file."
  (interactive)
  (load-file user-init-file))

;; キーバインド設定
(global-set-key (kbd "C-c d") 'duplicate-line)
(global-set-key (kbd "C-c s") 'delete-trailing-whitespace-and-save)
(global-set-key (kbd "C-x 2") 'split-window-below-and-switch)
(global-set-key (kbd "C-x 3") 'split-window-right-and-switch)
(global-set-key (kbd "C-c i") 'open-init-file)
(global-set-key (kbd "C-c r") 'reload-init-file)

;; バッファ管理関数
(defun kill-other-buffers ()
  "Kill all buffers except the current one."
  (interactive)
  (mapc 'kill-buffer 
        (delq (current-buffer) 
              (remove-if-not 'buffer-file-name (buffer-list)))))

(defun switch-to-previous-buffer ()
  "Switch to the previous buffer."
  (interactive)
  (switch-to-buffer (other-buffer (current-buffer) 1)))

(global-set-key (kbd "C-c k") 'kill-other-buffers)
(global-set-key (kbd "C-c b") 'switch-to-previous-buffer)

プログラミング環境設定

;; プログラミング言語設定

;; Python設定
(use-package python-mode
  :ensure t
  :mode "\\.py\\'"
  :hook (python-mode . lsp-deferred)
  :config
  (setq python-indent-offset 4))

;; JavaScript/TypeScript設定
(use-package js2-mode
  :ensure t
  :mode "\\.js\\'"
  :hook (js2-mode . lsp-deferred)
  :config
  (setq js2-basic-offset 2))

(use-package typescript-mode
  :ensure t
  :mode "\\.ts\\'"
  :hook (typescript-mode . lsp-deferred)
  :config
  (setq typescript-indent-level 2))

;; Web開発
(use-package web-mode
  :ensure t
  :mode (("\\.html?\\'" . web-mode)
         ("\\.css\\'" . web-mode)
         ("\\.jsx?\\'" . web-mode)
         ("\\.tsx?\\'" . web-mode))
  :config
  (setq web-mode-markup-indent-offset 2)
  (setq web-mode-css-indent-offset 2)
  (setq web-mode-code-indent-offset 2))

;; Markdown
(use-package markdown-mode
  :ensure t
  :mode (("\\.md\\'" . markdown-mode)
         ("\\.markdown\\'" . markdown-mode))
  :config
  (setq markdown-command "multimarkdown"))

;; YAML
(use-package yaml-mode
  :ensure t
  :mode "\\.ya?ml\\'")

;; JSON
(use-package json-mode
  :ensure t
  :mode "\\.json\\'")

;; フライチェック(シンタックスチェック)
(use-package flycheck
  :ensure t
  :init (global-flycheck-mode)
  :config
  (setq-default flycheck-disabled-checkers '(emacs-lisp-checkdoc)))

;; 自動フォーマット
(use-package format-all
  :ensure t
  :hook (prog-mode . format-all-mode))