Emacs Configuration @minorugh

1. はじめに

  • Leaf に乗り換えたのを機に大幅に整理したので RTDフォームでまとめました。
  • 設定ファイルの全てを ~/Dropbox/repo/github.com/minorugh/dotfiles/.emacs.d/ に置いて Git管理し、そのシンボリックを ~/ に貼ることで複数端末で共有しています。
  • 設定内容も本ドキュメントも、@takaxp さんの init.el の記事から多くを吸収した模倣版です。 本家と重複する説明は省き、執筆用途にカスタマイズしたポイントのみ補足説明します。
  • 設定ファイル本体は、GitHub に公開しています。

八十路も近い老骨ながら、@masasam さん、@takaxp さんのご指導を得て、盲目的なパッチワークから多少なりとも自力でカスタマイズできるまで進化できました。感謝!

ファイル配置のデレクトリ構成は以下のとおりです。

~/.emacs.d
├── el-get/
│   └── el-get で導入したpackageはここに配置
├── elpa/
├── info/
├── inits/
│   ├── 00_base.el
│   ├── 01_git.el
│   ├── ...
│   ├── 90_translate.el
│   └── 99_dashboard.el
├── snippets/
├── init.el
└── init-mini.el

2. 起動設定

基本的には init.el を読み込むことで制御しています。 手順は以下のとおり。

  1. init.el の読み込み
  2. inits/ のファイル群を読み込み (init-loader 使用)

init-loader を使うことの是非については諸説あるようですが、多くの恩恵は捨て難く私には必須ツールです。

2.1 起動の高速化

我流なのおすすめできませんが、ごく簡単な概念でかなり効果的ですので説明しておきます。

  1. init.el の冒頭設定で超大胆に GC を減らします。
    (setq gc-cons-threshold  (* 1024 1024 100))
  2. 次にパッケージなどの基本的な初期設定を読み込みます。
  3. 残りの設定ファイル群をinit-loaderでまとめて after-init-hook で遅延読み込みします。
  4. 全て読み終わったら、emacs-startup-hook で GC の値をdefaultに戻します。

after-init-hook が実行されたあとに emacs-startup-hook が実行されるという仕組みを利用してわけです。

;; Speed up startup
(defvar default-file-name-handler-alist file-name-handler-alist)
(defvar default-gc-cons-threshold gc-cons-threshold)
(setq file-name-handler-alist nil)
(setq gc-cons-threshold (* 1024 1024 100))
(add-hook
 'emacs-startup-hook
 (lambda ()
   "Restore defalut values after startup."
   (setq file-name-handler-alist default-file-name-handler-alist)
   (setq gc-cons-threshold default-gc-cons-threshold)))
(leaf init-loader
  :ensure t
  :config
  (custom-set-variables '(init-loader-show-log-after-init 'error-only))
  (add-hook 'after-init-hook
			(lambda () (init-loader-load))))

2.3 初期画面設定

起動時にギクシャクする画面は見たくないので、init.el の冒頭に以下を設定しています。

;; Disable UI
(set-frame-parameter nil 'fullscreen 'maximized)
(scroll-bar-mode 0)
(tool-bar-mode 0)
(menu-bar-mode 0)
(setq inhibit-splash-screen t
	  inhibit-startup-message t)

設定順序など影響ないと思っていたのですが、いろいろ試した結果上記の順番が一番起動時間が早かったです。理由は私にはわかりません。

2.4 Dashboard バッファーをリロードさせる

Emacs 起動時の初期画面には、Dashboard を表示させています。

Dashboard by iceberg-theme

愛着あるこのバッファーですが、うっかり Killすると消えてしまうので消さないでリロードできるように設定しました。Emacs での作業が一段落したらここへ戻ります。

下記設定例では <Home> キーを押すことで、直前に作業していたバッファー画面とDashboard画面とをトグルで表示させています。

(leaf dashboard :ensure t
  :config
  (with-eval-after-load 'dashboard
    (bind-key "<home>" 'open-dashboard)
    (bind-key "<home>" 'quit-dashboard dashboard-mode-map))
  ;;  (中略)
  )

(leaf *dasuboard-extension
  :config
  (defvar dashboard-recover-layout-p nil
	"Wether recovers the layout.")

  (defun restore-previous-session ()
	"Restore the previous session."
	(interactive)
	(when (bound-and-true-p persp-mode)
	  (restore-session persp-auto-save-fname)))

  (defun restore-session (fname)
	"Restore the specified session."
	(interactive
	 (list (read-file-name "Load perspectives from a file: " persp-save-dir)))
	(when (bound-and-true-p persp-mode)
	  (message "Restoring session...")
	  (quit-window t)
	  (condition-case-unless-debug err
		  (persp-load-state-from-file fname)
		(error "Error: Unable to restore session -- %s" err))
	  (message "Done")))

  (defun open-dashboard ()
	"Open the *dashboard* buffer and jump to the first widget."
	(interactive)
	(delete-other-windows)
	(setq default-directory "~/")
	;; Refresh dashboard buffer
	(if (get-buffer dashboard-buffer-name)
		(kill-buffer dashboard-buffer-name))
	(dashboard-insert-startupify-lists)
	(switch-to-buffer dashboard-buffer-name)
	;; Jump to the first section
	(goto-char (point-min))
	(dashboard-goto-recent-files))

  (defun quit-dashboard ()
	"Quit dashboard window."
	(interactive)
	(quit-window t)
	(when (and dashboard-recover-layout-p
			   (bound-and-true-p winner-mode))
	  (winner-undo)
	  (setq dashboard-recover-layout-p nil)))

  (defun dashboard-goto-recent-files ()
	"Go to recent files."
	(interactive)
	(funcall (local-key-binding "r"))))

2.5 [init-mini.el] 最小限のEmacsを起動

init-mini.el は、最小限の emacs を起動させるための設定です。

新しいパッケージや設定をテストしたり、エラー等で Emacsが起動しない場合に使用します。 シェルから eq と入力することで起動することがでます。

以下を .zshrc または .bashrc に書き込みます。

alias eq='emacs -q -l ~/.emacs.d/init-mini.el'

ファイルの PATH は、ご自分の環境に応じて修正が必要です。

3. コア設定

Emacs を操作して文書編集する上で必要な設定。

3.1 言語 / 文字コード

シンプルにこれだけです。

(set-language-environment "Japanese")
(prefer-coding-system 'utf-8)

3.2 日本語入力

Debian10 にインストールした Emacs上で emacs-mozc を使っています。

3.2.1 EmacsのときはインラインXIMを無効にする

Emacsをソースからビルドするときに --without-xim しなかったので、インラインXIMでも日本語入力ができてしまいます。 特に使い分けする必要もなく紛らわしいので .Xresources で XIM無効化の設定をしました。

! ~/.Xresources
! Emacs XIMを無効化
Emacs*useXIM: false

3.2.2 [mozc] 句読点では即確定させる

句読点などを入力したとき、わざわざmozcに変換してもらう必要はないので以下を設定しておくことでワンアクションスピーディーになります。

;; Confirmed immediately
(define-key mozc-mode-map "," '(lambda () (interactive) (mozc-insert-str "、")))
(define-key mozc-mode-map "." '(lambda () (interactive) (mozc-insert-str "。")))
(define-key mozc-mode-map "?" '(lambda () (interactive) (mozc-insert-str "?")))
(define-key mozc-mode-map "!" '(lambda () (interactive) (mozc-insert-str "!")))
(defun mozc-insert-str (str)
  "If punctuation marks, immediately confirm."
  (mozc-handle-event 'enter)
  (toggle-input-method)
  (insert str)
  (toggle-input-method))

3.2.3 [mozc] ユーザー辞書を複数端末で共有させる

Windows10 では Google日本語入力 を使います。WSLも含めて複数環境で Emacsを動かしています。これら全ての環境で Mozcユーザー辞書を共有できるようにしたいと思考中です。

Mozc のファイル群を Dropbox に保存して、シンボリックを各端末に置くという方法で一応は動きますが、同時使用すると競合コピーが作られるので多少問題もあります。辞書の共有は、Google Drive がよいという情報もあるので、時間のあるときにゆっくり試してみます。

4. カーソル移動

カーソルの移動は、原則デフォルトで使っていますが、以下の挙動だけ変更しています。

ウインドウ移動 C-q
バッファー切り替え M-] / M-[
バッファー先頭・末尾 C-a / C-e
編集点の移動 C-x C-x

4.1 ウインドウの移動

私の場合、二分割以上の作業はしないので C-q だけで便利に使えるこの関数は宝物です。 最初の C-q でに分割になり、二度目以降は押すたびに Window 移動します。

(defun other-window-or-split ()
  "If there is one window, open split window.
If there are two or more windows, it will go to another window."
  (interactive)
  (when (one-window-p)
    (split-window-horizontally))
  (other-window 1))
(bind-key "C-q" 'other-window-or-split)

4.2 [sequential-command.el] バッファー先頭・末尾

sequential-command.el は地味なながら一度使うと便利すぎて止められません。

(leaf sequential-command
  :ensure t
  :config
  (bind-key "C-a" 'seq-home)
  (bind-key "C-e" 'seq-end)
  (leaf sequential-command-config
	:hook (emacs-startup-hook . sequential-command-setup-keys)))

4.3 [expand-region]カーソル位置を起点に選択範囲を賢く広げる

er/expand-region を呼ぶと、カーソル位置を起点として前後に選択範囲を広げてくれます。2回以上呼ぶとその回数だけ賢く選択範囲が広がりますが、2回目以降は設定したキーバインドの最後の一文字を連打すればOKです。その場合、選択範囲を狭める時は - を押し, 0 を押せばリセットされます。

(leaf expand-region
  :ensure t
  :bind ("C-@" . er/expand-region))

5. 編集サポート

5.1 [selected] リージョン選択時のアクションを制御

選択領域に対するスピードコマンドです。Emacsバッファーで領域を選択した後、バインドしたワンキーを入力するとコマンドが実行されます。

コマンドの数が増えてきたら、ヘルプ代わりに使える counsel-selected も便利そうです。

(leaf selected
  :ensure t
  :global-minor-mode selected-global-mode
  :config
  (bind-key ";" 'comment-dwim selected-keymap)
  (bind-key "d" 'clipboard-kill-region selected-keymap)
  (bind-key "f" 'describe-function selected-keymap)
  (bind-key "v" 'describe-variable selected-keymap)
  (bind-key "c" 'clipboard-kill-ring-save selected-keymap)
  (bind-key "i" 'iedit-mode selected-keymap)
  (bind-key "s" 'swiper-thing-at-point selected-keymap)
  (bind-key "k" 'my:koujien selected-keymap)
  (bind-key "e" 'my:eijiro selected-keymap)
  (bind-key "w" 'my:weblio selected-keymap)
  (bind-key "t" 'google-translate-auto selected-keymap)
  (bind-key "g" 'my:google selected-keymap))

検索結果を browse-url で表示させるユーザーコマンドは、検索urlのフォーマとさえわかれば、パッケージツールに頼らずともお好みのマイコマンドを作成できます。

以下は、google と Weblio串刺し検索の例です。

(leaf cus-selected
  :init
  ;; Control mozc when seleceted
  (defun my:activate-selected ()
	(selected-global-mode 1)
	(selected--on)
	(remove-hook 'activate-mark-hook #'my:activate-selected))
  (add-hook 'activate-mark-hook #'my:activate-selected)
  (defun my:ime-on ()
	(interactive)
	(when (null current-input-method) (toggle-input-method)))
  (defun my:ime-off ()
	(interactive)
	(inactivate-input-method))

  (defvar my:ime-flag nil)
  (add-hook
   'activate-mark-hook
   #'(lambda ()
	   (setq my:ime-flag current-input-method) (my:ime-off)))
  (add-hook
   'deactivate-mark-hook
   #'(lambda ()
	   (unless (null my:ime-flag) (my:ime-on))))

  ;; User-functions-for-selected
  (defun my:koujien (str)
	(interactive (list (my:get-region nil)))
	(browse-url (format "https://sakura-paris.org/dict/広辞苑/prefix/%s"
						(upcase (url-hexify-string str)))))

  (defun my:weblio (str)
	(interactive (list (my:get-region nil)))
	(browse-url (format "https://www.weblio.jp/content/%s"
						(upcase (url-hexify-string str)))))

  (defun my:eijiro (str)
	(interactive (list (my:get-region nil)))
	(browse-url (format "https://eow.alc.co.jp/%s/UTF-8/"
						(upcase (url-hexify-string str)))))

  (defun my:google (str)
	(interactive (list (my:get-region nil)))
	(browse-url (format "https://www.google.com/search?hl=ja&q=%s"
						(upcase (url-hexify-string str)))))

  (defun my:get-region (r)
	"Get search word from region."
	(buffer-substring-no-properties (region-beginning) (region-end))))

5.2 [selected] IME のオン・オフを自動制御する

selected.el は、うっかり IMEオンのまま選択領域に対するコマンドを選択すると、押下キーがバッファにそのまま入力されてしまいます。

領域を選択し始める時にIMEをオフにして、コマンド発行後にIMEを元に戻すという例が、 @takaxp さんの Qiitaの記事 にあったので、私の環境(emacs-mozc )にあうように設定したら、すんなり動いてくれました。感謝!

(defun my-activate-selected ()
  (selected-global-mode 1)
  (selected--on) ;; must call expclitly here
  (remove-hook 'activate-mark-hook #'my-activate-selected))
(add-hook 'activate-mark-hook #'my-activate-selected)

(defun my:ime-on ()
  (interactive)
  (when (null current-input-method) (toggle-input-method)))
(defun my:ime-off ()
  (interactive)
  (inactivate-input-method))

(defvar my:ime-flag nil)
(add-hook
 'activate-mark-hook
 #'(lambda ()
	 (setq my:ime-flag current-input-method) (my:ime-off)))
(add-hook
 'deactivate-mark-hook
 #'(lambda ()
	 (unless (null my:ime-flag) (my:ime-on))))

5.3 [darkroom-mode] 執筆モード

darkroom.el は、画面の余計な項目を最小限にして、文章の執筆に集中できるようにするパッケージです。

基本機能は、文字サイズが大きくなり、モード行が消えるだけですが、設定例では、行番号表示、diff-hl、flymake も消しています。併せて文字間隔も少し大きくして読みやすくしました。[F12] キーで IN/OUT をトグルしています。

(leaf darkroom :ensure t
  :config
  (bind-key "<f12>" 'my:darkroom-mode-in)
  (defun my:darkroom-mode-in ()
    "Darkroom mode in."
    (interactive)
    (display-line-numbers-mode 0)
    (diff-hl-mode 0)
    (flymake-mode 0)
    (setq line-spacing 0.4)
    (darkroom-mode 1)
    (bind-key "<f12>" 'my:darkroom-mode-out darkroom-mode-map))
  (defun my:darkroom-mode-out ()
    "Darkroom mode out."
    (interactive)
    (darkroom-mode 0)
    (flymake-mode 1)
    (diff-hl-mode 1)
    (setq line-spacing 0.1)
    (display-line-numbers-mode 1)))

5.4 [yatex] YaTexでTex編集

ごく一般的な設定例ですが、参考になるとしたら yatexprcM-x YaTeX-lpr を使って一気にPDF作成まで自動化している点でしょうか。

(leaf yatex
  :ensure t
  :mode ("\\.tex\\'" . yatex-mode)
  :init
  (setq tex-command "platex")
  (setq dviprint-command-format "dvpd.sh %s")
  (setq YaTeX-kanji-code nil)
  (setq YaTeX-latex-message-code 'utf-8)
  (setq YaTeX-default-pop-window-height 15)
  :config
  (when (require 'yatexprc nil t)
	(bind-key "M-c" 'YaTeX-typeset-buffer)
	(bind-key "M-l" 'YaTeX-lpr)))

YaTeX-lpr は、dviprint-command-format を呼び出すコマンドです。

dviファイルからdvipdfmx でPDF作成したあと、PDFビューアーを起動させて表示させるところまでをバッチファイルに書き、PATHの通ったところに置きます。私は、/usr/loca/bin に置きました。

#!/bin/bash
name=$1
dvipdfmx $1 && evince ${name%.*}.pdf
# Delete unnecessary files
rm *.au* *.dv* *.lo*

上記の例では、ビューアーに Linux の evince を設定していますが、mac でプレビューを使う場合は、下記のようになるかと思います。

dvipdfmx $1 && open -a Preview.app ${name%.*}.pdf

6. 表示サポート

6.1 [emacs-lock-mode] scratch バッファーを消さない

難しい関数を設定せずとも内蔵コマンドで簡単に実現できます。

;; Set buffer that can not be killed
(with-current-buffer "*scratch*"
  (emacs-lock-mode 'kill))
(with-current-buffer "*Messages*"
  (emacs-lock-mode 'kill))

6.2 swiper を migemo 化してローマ字入力で日本語を検索

昔は、avy-migemo-e.g.swiper.el を使って出来ていたのですが、2年ほど前から更新が止まってしまっていて、いまは動きません。つい最近、avy-migemo を使わない Tipsを見つけたので試した処、機嫌よく動いてくれています。

(defun my:ivy-migemo-re-builder (str)
  (let* ((sep " \\|\\^\\|\\.\\|\\*")
		 (splitted
		  (--map (s-join "" it)
				 (--partition-by
				  (s-matches-p " \\|\\^\\|\\.\\|\\*" it)
				  (s-split "" str t)))))
    (s-join "" (--map (cond
					   ((s-equals? it " ") ".*?")
					   ((s-matches? sep it) it)
					   (t (migemo-get-pattern it)))
					  splitted))))

(setq ivy-re-builders-alist
	  '((t . ivy--regex-plus)
		(swiper . my:ivy-migemo-re-builder)))

7. Hydra

hydra.el は、連続して操作するときにプレフィクスキーをキャンセルさせるための elispです。

日本では、smartrep.el が有名だったようですが、hydra.elも同様の機能を提供します。 私はおもに8種の hydra を設定しています。それぞれを呼び出すための相関図は下記のとおりです。

┌──────────────────┐
│ hydra-work-menu  │ ワークテーブル分岐
└──────────────────┘
↓↑ 相互に行き来できる
┌──────────────────┐
│ hydra-quick-menu │ よく使うコマンド群
└──────────────────┘
   │
   ├── hydra-compile
   ├── hydra-markdown
   ├── hydra-package
   ├── hydra-magit    <<- Dired からも呼び出せる
   ├── hydra-browse   <<- Dashboard からも呼び出せる
   └── hydra-pinky

7.1 [hydra-work-menu / hydra-quick-menu] コマンドメニュー

[hydra-work-menu](el](https://github.com/abo-abo/hydra) は、連続して操作するときにプレフィクスキーをキャンセルさせるための elispです。

日本では、smartrep.el が有名だったようですが、hydra.elも同様の機能を提供します。 私はおもに8種の hydra を設定しています。それぞれを呼び出すための相関図は下記のとおりです。

┌──────────────────┐
│ hydra-work-menu  │ ワークテーブル分岐
└──────────────────┘
↓↑ 相互に行き来できる
┌──────────────────┐
│ hydra-quick-menu │ よく使うコマンド群
└──────────────────┘
   │
   ├── hydra-compile
   ├── hydra-markdown
   ├── hydra-package
   ├── hydra-magit    <<- Dired からも呼び出せる
   ├── hydra-browse   <<- Dashboard からも呼び出せる
   └── hydra-pinky

7.1 [hydra-work-menu / hydra-quick-menu] コマンドメニュー

hydra-work-menu には、 日記や俳句関係のシリーズ記事の追加、編集など、毎日頻繁に開くワークスペースへのショートカットを設定しています。

hydra-work-menu

hydra-quick-menu の方には、 編集作業で頻繁に使うツール群のほか、my:dired を開くためのショートカットも設定しています。

hydra-quick-menu

この2つの hydra は、いわば私の秘書のような役割で、どちらからでも相互にトグルで呼び出せるようにしています。

7.2 [hydra-extention] その他の Hydra 設定。

hydra で工夫するといろんなコマンドのキーバインドを記憶する必要もなく GUI 感覚で操作できるので積極的に使っています。

Qitta に詳しい記事を書いています。

8. 履歴 / ファイル管理

8.1 [auto-save-buffer-enhanced] ファイルの自動保存

auto-save-buffer-enhanced.el は、Emacs に本当の自動保存機能を提供します。

Tramp-mode と併用すると emacs が固まってしまうことがあるようなので、 auto-save-buffers-enhanced-exclude-regexps を設定して trampでリモート編集時には auto-save-buffers を停止するようにしています。

(leaf auto-save-buffers-enhanced :ensure t
  :config
  (setq auto-save-buffers-enhanced-quiet-save-p t)
  ;; Exclusion of the auto-save-buffers
  (setq auto-save-buffers-enhanced-exclude-regexps '("^/ssh:" "^/scp:" "/sudo:"))
  (auto-save-buffers-enhanced t))

8.2 空になったファイルを自動的に削除

howm や org でメモをとるときに、ゴミファイルが残らないように時々メンテしています。ファイルを開いて中味を確認してから、一度閉じて dited で削除するというプロセスは手間がかかりすぎます。

下記の設定をしておくと、C-x h で全選択して delete したあと kill-buffer することで自動的にファイルが削除されるので便利です。

(defun my:delete-file-if-no-contents ()
  "Automatic deletion for empty files (Valid in all modes)."
  (when (and (buffer-file-name (current-buffer))
			 (= (point-min) (point-max)))
    (delete-file
     (buffer-file-name (current-buffer)))))
(if (not (memq 'my:delete-file-if-no-contents after-save-hook))
    (setq after-save-hook
		  (cons 'my:delete-file-if-no-contents after-save-hook)))

8.3 [undo-tree]

C-x -uundo-tree-visualize を呼ぶと tee の下に diff表示もしてくれるように設定しています。

(leaf undo-tree
  :ensure t
  :hook ((prog-mode-hook text-mode-hook) . undo-tree-mode)
  :config
  (bind-key* "C-_" 'undo-tree-undo)
  (bind-key* "C-\\" 'undo-tree-undo)
  (bind-key* "C-/" 'undo-tree-redo)
  (bind-key* "C-x u" 'undo-tree-visualize)
  :init
  (make-variable-buffer-local 'undo-tree-visualizer-diff)
  (setq-default undo-tree-visualizer-diff t)
  (setq undo-tree-visualizer-timestamps t)
  (setq undo-tree-visualizer-diff t)
  (setq undo-tree-enable-undo-in-region nil)
  (setq undo-tree-auto-save-history nil)
  (setq undo-tree-history-directory-alist
		`(("." . ,(concat user-emacs-directory "undo-tree-hist/")))))

8.4 [direx]

作業中に同じプロジェクト内の他のファイルを参照したい時に便利に使えます。当初は、neotreeを使っていましたが、大容量のフォルダーを扱うときなど動作が重くなってイライラするのでこちらを愛用しています。

(defun direx:jump-to-project-directory ()
  "If in project, launch direx-project otherwise start direx."
  (interactive)
  (let ((result	(ignore-errors (direx-project:jump-to-project-root-other-window) t)))
    (unless result (direx:jump-to-directory-other-window))))

(defun direx:open-file ()
  "In direx, open the file in associated application."
  (interactive)
  (let* ((item (direx:item-at-point!))
		 (file (direx:item-tree item))
		 (file-full-name (direx:file-full-name file)))
    (unless (getenv "WSLENV")
      (call-process "xdg-open" nil 0 nil file-full-name))
    ;; use wsl-utils:https://github.com/smzht/wsl-utils
    (when (getenv "WSLENV")
      (call-process "wslstart" nil 0 nil file-full-name))))

(leaf direx :ensure t :require t
  :config
  (bind-key "<f10>" 'direx:jump-to-project-directory)
  (bind-key "C-x C-j" 'direx:jump-to-project-directory)
  (define-key direx:direx-mode-map "o" 'direx:open-file)
  (define-key direx:direx-mode-map "SPC" 'direx:find-item-other-window)
  (define-key direx:direx-mode-map "<f10>" 'quit-window)
  (setq direx:leaf-icon "  " direx:open-icon "📂" direx:closed-icon "📁")
  (push '(direx:direx-mode :position left :width 25 :dedicated t)
		popwin:special-display-config))

9. 開発サポート

9.1 dotfiles

記事準備中

9.2 [magit]

magitの画面は、デフォルトでは、other-window に表示されますが、フルフレームで表示されるようにカスタマイズしています。

(setq magit-display-buffer-function #'magit-display-buffer-fullframe-status-v1)

10. メモ環境

10.1 Org Mode / Howm Mode

私の場合、専門的なプログラミングもやらないし、現役引退の身なのでGTDとかも必要ありません。Emacsはメモ書きに特化した使い方です。メモは全てhowmを使ってmarkdown記法で書いています。メモリストでカテゴリを分かりやすくするためのタグ付けをするのでメニュー機能としてorg-captureを使います。

captureでメモ機能を発動させると画面が半分になるのがいやなので、最大化で開くようにしました。また、dashboard画面に簡単なタスクを表示するにorg-agendaも使ーっています。

(leaf howm
  :ensure t
  :hook (emacs-startup-hook . howm-mode)
  :chord ("@@" . howm-list-all)
  :init
  (setq howm-view-title-header "#")
  (defun my:howm-create-file ()
    "Make howm create file with 'org-capture'."
    (interactive)
    (format-time-string "~/Dropbox/howm/%Y/%m/%Y%m%d%H%M.md" (current-time)))
  :config
  (bind-key [backtab] 'howm-view-summary-previous-section howm-view-summary-mode-map)
  (setq howm-directory "~/Dropbox/howm")
  (setq howm-view-split-horizontally t)
  (setq howm-view-summary-persistent nil)
  (setq howm-normalizer 'howm-sort-items-by-reverse-date)
  (setq howm-user-font-lock-keywords
		'(("memo:" . (0 'gnus-group-mail-3))
		  ("note:" . (0 'epa-mark))
		  ("perl:" . (0 'diff-refine-added))
		  ("haiku:" . (0 'compilation-mode-line-exit))
		  ("emacs:" . (0 'compilation-info))
		  ("linux:" . (0 'compilation-error)))))

(leaf org
  :config
  (bind-key "C-c a" 'org-agenda)
  (bind-key "C-c c" 'org-capture)
  (setq org-log-done 'time)
  (setq org-use-speed-commands t)
  (setq org-src-fontify-natively t)
  (setq org-agenda-files '("~/Dropbox/howm/org/task.org"))
  (setq org-capture-templates
		'(("t" " Task" entry (file+headline "~/Dropbox/howm/org/task.org" "Task")
		   "** TODO %?\n SCHEDULED: %^t \n" :prepend t)
		  ("m" " Memo" plain (file my:howm-create-file)
		   "# memo: %?\n%U %i")
		  ("n" " Note" plain (file my:howm-create-file)
		   "# note: %?\n%U %i")
		  ("p" "★ Perl" plain (file my:howm-create-file)
		   "# Perl: %?\n%U %i\n\n>>>\n\n```perl\n%i\n```")
		  ("e" "★ Emacs" plain (file my:howm-create-file)
		   "# emacs: %?\n%U %i\n\n```emacs-lisp\n%i\n```")
		  ("l" "★ Linux" plain (file my:howm-create-file)
		   "# linux: %?\n%U %i")))
  :init
  ;; Maximize the org-capture buffer
  (defvar my:org-capture-before-config nil
    "Window configuration before 'org-capture'.")
  (defadvice org-capture (before save-config activate)
    "Save the window configuration before 'org-capture'."
    (setq my:org-capture-before-config (current-window-configuration)))
  (add-hook 'org-capture-mode-hook 'delete-other-windows))

10.2 open-junk-file

junkファイルの保存もhowmフォルダーに置くことで、howmの検索機能が利用できて便利です。また、直近のjunkファイルを即開けるように open-last-junk-file を定義しました。

(leaf open-junk-file :ensure t
  :config
  (setq open-junk-file-format "~/Dropbox/howm/junk/%Y%m%d.")
  (setq open-junk-file-find-file-function 'find-file)
  :init
  ;; https://qiita.com/zonkyy/items/eba6bc64f66d278f0032
  (leaf em-glob	:require t
	:config
	(defvar junk-file-dir "~/Dropbox/howm/junk/")
	(defun open-last-junk-file ()
	  "Open last created junk-file."
	  (interactive)
	  (find-file
	   (car
		(last (eshell-extended-glob
			   (concat
				(file-name-as-directory junk-file-dir)
				"*.*.*"))))))))

10.3 scratch buffer

メールやWEBなど全てをEmacsでという思想もありますが、窮屈なので私好みではありません。少し長めのメール文章などは、scratch バッファーに書いてコピペします。また、scratchを付箋代わりにも使うので、自動保存のために persistent-scratch.el を使います。

scratchバッファーの永続化にはいろいろTipsもあるようですが、標準機能で簡単に設定できます。なんだかんだで便利なscratchなので toggle-scratch を設定して愛用しています。

;; Set buffer that can not be killed
(with-current-buffer "*scratch*"
  (emacs-lock-mode 'kill))
(with-current-buffer "*Messages*"
  (emacs-lock-mode 'kill))

(leaf persistent-scratch
  :ensure t
  :config
  (persistent-scratch-setup-default))

(bind-key
 [S-return]
 (defun toggle-scratch ()
   "Toggle current buffer and *scratch* buffer."
   (interactive)
   (if (not (string= "*scratch*" (buffer-name)))
	   (progn
		 (setq toggle-scratch-prev-buffer (buffer-name))
		 (switch-to-buffer "*scratch*"))
	 (switch-to-buffer toggle-scratch-prev-buffer))))

11. フォント / 配色関係

11.1 フォント設定

(add-to-list 'default-frame-alist '(font . "Cica-18"))
;; for sub-machine
(when (string-match "x250" (shell-command-to-string "uname -n"))
  (add-to-list 'default-frame-alist '(font . "Cica-15")))

11.2 [volatile-highlight]コピペした領域を強調

コピペした領域をフラッシングさせます。

(leaf volatile-highlights :ensure t
  :config
  (volatile-highlights-mode)
  (with-no-warnings
    (when (fboundp 'pulse-momentary-highlight-region)
      (defun my-vhl-pulse (beg end &optional _buf face)
		"Pulse the changes."
		(pulse-momentary-highlight-region beg end face))
      (advice-add #'vhl/.make-hl :override #'my-vhl-pulse))))

12. ユーティリティー関数

12.1 Terminal を Emacsから呼び出す

(defun term-current-dir-open ()
  "Open terminal application in current dir."
  (interactive)
  (let ((dir (directory-file-name default-directory)))
    (compile (concat "gnome-terminal --working-directory " dir))))
(bind-key "<f4>" 'term-current-dir-open)

12.2 Thunar を Emacsから呼び出す

(defun filer-current-dir-open ()
  "Open filer in current dir."
  (interactive)
  (compile (concat "Thunar " default-directory)))
(bind-key "<f3>" 'filer-current-dir-open)

12.7 [restart-emacs] Emacsを再起動する

C-x C-c は、デフォルトで (save-buffers-kill-emacs) に割り当てられていますが、Emacsの再起動にリバインドしました。

(leaf restart-emacs :ensure t
  :bind (("C-x C-c" . restart-emacs)))

13. おわりに

以上が私の init.el とその説明です。

私の Emacsは、Webページのメンテナンスがメインで、プログラムミング・エディタというよりは、「賢くて多機能なワープロ」という存在です。ありえない…ような邪道キーバインドや未熟な点も多々ありますが、諸先輩に学びながら育てていきたいと願っています。