はじめに
年末にemacsのライティング環境整備(2025-2026)を書いてから、少し時間をかけてinit.elファイルの見直しを行いました。
見直しに際し、留意した点は以下のとおりです:
- 長年放置していたパッケージの扱いを、可能な限り
use-packageに統一する - 使用頻度が低いパッケージについては、遅延読み込み設定を徹底する
- 設定やパッケージの読み込みの順番について、整合性を持たせる
- 新たな便利関数を追加する
- 自前関数の命名の見直しを行う
- ショートカットキーの整合性について見直しを行う
- prefix
Ctrl-z: 使用頻度の高い外部パッケージと自前関数にあてる - prefix
Ctrl-t: 主にorg-modeとdenote関連に当てる
- prefix
以前の投稿と同様、このinit.elの特徴は以下のとおりです:
- コメントを含め1,500行程度で、導入パッケージもそれほど多くありません
-
文系の研究者や翻訳者に便利なパッケージ(電子辞書、翻訳、pdf、epubリーダーなど)の設定と関数定義が含まれています
- 複雑な数式やチャートを書く機会が少ないため、LaTex環境は考慮していません
- 全般にProtesilaos Stavrou(
modus系のテーマやdenoteの作者で、キプロスの哲学者/詩人/プログラマ)の影響を強く受けています - webや書籍で得たtipsを拝借した箇所も多いのですが、多くは引用元を辿れないため(いわゆる鰻のタレ状態)、特別なケースを除き、ソースへの参照は断念しました(偉大な先人に感謝します)
- 設定や記法に問題があるかもしれませんが(少なくともわたくしの環境では)初期化および運用時に重大なエラーは発生していません
長くなりますが、みなさまの参考になれば幸いです。
Linux環境
現在のメイン執筆環境は以下のとおりです:
🌜 fastfetch
██████████████████ ████████ xxx@t430
██████████████████ ████████ -------
██████████████████ ████████ OS: Manjaro Linux x86_64
██████████████████ ████████ Host: xxxxxxx (ThinkPad T430)
████████ ████████ Kernel: Linux 6.12.73-1-MANJARO
████████ ████████ ████████ Uptime: 17 hours, 55 mins
████████ ████████ ████████ Packages: 2068 (pacman)[stable]
████████ ████████ ████████ Shell: zsh 5.9
████████ ████████ ████████ Display (SEC324C): 1600x900 in 14", 60 Hz [Built-in]
████████ ████████ ████████ DE: Xfce4 4.20
████████ ████████ ████████ WM: Xfwm4 (X11)
████████ ████████ ████████ WM Theme: WhiteSur-Dark-solid-nord
████████ ████████ ████████ Theme: Fusion [Qt], WhiteSur-Dark-solid-nord [GTK2/3/4]
████████ ████████ ████████ Icons: Papirus-Dark [Qt], Papirus-Dark [GTK2/3/4]
Font: Noto Sans CJK JP (8pt, Meium) [Qt], Noto Sans CJK JP (8pt, Medium) [GTK2/3/4]
Cursor: volantes
Terminal: tmux 3.6a
CPU: Intel(R) Core(TM) i5-3320M (4) @ 3.30 GHz
GPU: Intel 3rd Gen Core processor Graphics Controller @ 1.20 GHz [Integrated]
Memory: 2.34 GiB / 15.31 GiB (15%)
Swap: 0 B / 4.00 GiB (0%)
Disk (/): 84.08 GiB / 439.05 GiB (19%) - ext4
Disk (/mnt/Medium): 170.50 GiB / 439.54 GiB (39%) - ext4
Local IP (enp0s25): 192.168.xxx.xxx/24
Battery (45N1015): 74% [AC Connected]
Locale: ja_JP.UTF-8
- ご覧の通りロースペックPCを10年来使用していますが、快適に動作しています(時間のかかるコンパイルだけはデスクトップ機に任せています)
init.el
ここから設定を書いていきます。
実行パスや環境変数、および特定のアプリケーションを呼び出すパッケージの設定については、ご自身の環境に合わせて適宜変更してください。
0. emacs起動中のGCの最適化
;;; === INITIAL OPTIMIZATION ===
(setq gc-cons-threshold most-positive-fixnum)
(setq read-process-output-max (* 1024 1024)) ;; 1MB
1. パッケージの初期設定
;; パッケージのアーカイブ
(setq package-archives
'(
("melpa" . "https://melpa.org/packages/")
("gnu" . "https://elpa.gnu.org/packages/")
("nongnu" . "https://elpa.nongnu.org/nongnu/")
))
;; use-packageを使用
(require 'use-package)
;; インストール時に不要な警告を出さないようにする
(add-to-list 'display-buffer-alist
'("\\`\\*\\(Warnings\\|Compile-Log\\)\\*\\'"
(display-buffer-no-window)
(allow-no-window . t)))
;; 自作パッケージのパスを追加
(add-to-list 'load-path "~/.emacs.d/mypac/")
;; 組み込みパッケージの更新
(setq package-install-upgrade-built-in t)
;; emacs本体のorg.elとのコンフリクト回避のため、先に読み込ませる
(use-package org
:ensure t
:demand t)
2. 日本語環境
-
Ctrl-oで日本語入力を切り替え -
mozcの変換候補はエコーエリアで表示
;; 言語環境はutf-8で統一
(setenv "LANG" "ja_JP.UTF-8")
(set-language-environment 'Japanese)
(set-default-coding-systems 'utf-8)
(set-terminal-coding-system 'utf-8)
(setq default-file-name-coding-system 'utf-8)
(setq default-process-coding-system '(utf-8 . utf-8))
(prefer-coding-system 'utf-8)
;; MOZCの設定
(use-package mozc
:ensure t
:demand t
:bind ("C-o" . toggle-input-method)
:config
(setq default-input-method "japanese-mozc")
(setq mozc-candidate-style 'echo-area)
(setq mozc-helper-program-name "mozc_emacs_helper")
(setq mozc-leim-title "🍙 "))
3. 全般的な挙動
;; 自動生成される設定はcustom.elで管理
(setq custom-file (locate-user-emacs-file "custom.el"))
(load custom-file :no-error-if-file-is-missing)
;; 右から左に読む言語に対応させないことで描画高速化
(setq-default bidi-display-reordering nil)
;; 同じ内容を履歴に記録しないようにする
(setq history-delete-duplicates t)
;; C-u C-SPC C-SPC ...で過去のマークを遡る
(setq set-mark-command-repeat-pop t)
;; filename<dir> 形式のバッファ名にする
(setq uniquify-buffer-name-style 'post-forward-angle-brackets)
(setq uniquify-ignore-buffers-re "[^*]+")
;; ロックファイルを作成しない
(setq create-lockfiles nil)
;; バックアップファイルの保存先
(setq backup-directory-alist '((".*" . "~/Medium/backup_original/local_data")))
;; 初期メッセージ無効化
(setq initial-scratch-message "")
;; 保存時にファイルの最後に常に改行を自動的に追加
(setq-default require-final-newline t)
;; インデントにはスペースを使用
(setq-default indent-tabs-mode nil)
(setq-default tab-width 2)
;; Warningの表示レベル
(setq warning-minimum-level :emergency)
(setq warning-suppress-types '((comp)))
;; ナローイングとワイドニング
(put 'upcase-region 'disabled nil)
(put 'narrow-to-region 'disabled nil)
;; ログの記録行数を増やす
(setq message-log-max 10000)
;; 履歴の記録数を増やす
(setq history-length 1000)
;; 警告音をオフにする
(setq ring-bell-function 'ignore)
;; yes/noをy/nに短縮
(setq use-short-answers t)
;; 他プロセスの編集をバッファに反映
(global-auto-revert-mode +1)
;; 選択状態で入力したときに、region領域を削除して挿入
(delete-selection-mode 1)
;; C-x C-bでibufferを使う
(global-set-key [remap list-buffers] 'ibuffer)
;; C-gをより使いやすく(Protesilaosのtipsを参照)
(defun prot/keyboard-quit-dwim ()
(interactive)
(cond
((region-active-p)
(keyboard-quit))
((derived-mode-p 'completion-list-mode)
(delete-completion-window))
((> (minibuffer-depth) 0)
(abort-recursive-edit))
(t
(keyboard-quit))))
(global-set-key (kbd "C-g") 'prot/keyboard-quit-dwim)
;; GCをgcmhに任せる
(use-package gcmh
:ensure t
:demand t
:config
(setq gcmh-idle-delay 10)
(setq gcmh-high-cons-threshold (* 100 1024 1024))
(gcmh-mode 1))
;; 実行パスの設定(zshの設定を認識しないケースがあるため)
(use-package exec-path-from-shell
:ensure t
:config
(when (memq window-system '(mac ns x))
(setq exec-path-from-shell-variables '("PATH" "MANPATH"))
(exec-path-from-shell-initialize))
(add-to-list 'exec-path (expand-file-name "~/.local/bin"))
(setenv "PATH" (concat (expand-file-name "~/.local/bin") ":" (getenv "PATH")))
(add-to-list 'exec-path (expand-file-name "~/.cargo/bin"))
(setenv "PATH" (concat (expand-file-name "~/.cargo/bin") ":" (getenv "PATH"))))
;; 最後のカーソル位置を保存
(use-package saveplace
:ensure nil
:init
(save-place-mode))
;; 削除したファイルをゴミ箱に移動させる
(setopt delete-by-moving-to-trash t)
;; キルリングに新しいテキストを追加する前にクリップボードの内容を保存
(setopt save-interprogram-paste-before-kill t)
;; ediff使用時に垂直分割する
(use-package ediff
:ensure t
:config
(setopt ediff-split-window-function 'split-window-horizontally))
4. 環境依存の設定
- X11デスクトップ環境で、クリップボード共有には
xclipを使用 - Wayland環境では
XWaylandを有効にすること
;; Shell設定
(setq explicit-shell-file-name "/usr/bin/zsh")
;; xclipを使ったクリップボードとの連携
(unless window-system
(when (getenv "DISPLAY")
(defun xclip-cut-function (text &optional push)
(with-temp-buffer
(insert text)
(call-process-region (point-min) (point-max) "xclip" nil 0 nil "-i" "-selection" "clipboard")))
(defun xclip-paste-function()
(let ((xclip-output (shell-command-to-string "xclip -o -selection clipboard")))
(unless (string= (car kill-ring) xclip-output)
xclip-output )))
(setq interprogram-cut-function 'xclip-cut-function)
(setq interprogram-paste-function 'xclip-paste-function)
(setopt save-interprogram-paste-before-kill t)))
;; xterm mouseのサポート
(use-package mouse
:ensure nil)
(xterm-mouse-mode t)
5. キー割り当てと便利な関数の導入
自分の指癖とコマンドの使用頻度に応じて設定しているため、そのままコピペするとオリジナル環境との整合性が崩れます。
;; C-zをprefixキーに割り当てる
(defvar my-keys-prefix-map (make-sparse-keymap)
"My personal keybindings prefix map.")
(define-key global-map (kbd "C-z") my-keys-prefix-map)
;; C-tをもう一つのprefixキーに割り当てる(主にdenote関連のコマンド向け)
(defvar my-keys-prefix-map-bis (make-sparse-keymap)
"My personal keybindings prefix map bis.")
(define-key global-map (kbd "C-t") my-keys-prefix-map-bis)
;; C-hでBackspace
(global-set-key (kbd "C-h") 'delete-backward-char)
;; 選択行のコピー(> 29.1: Ctrl+uで回数指定)
(global-set-key (kbd "C-c j") #'duplicate-dwim)
;; 画面の再描画
(global-set-key (kbd "<f7>") 'redraw-display)
;; C-x fをアンバインド(入力ミスが多いため)
(global-unset-key (kbd "C-x f"))
;; カーソル位置の前方を削除
(global-set-key (kbd "C-c u") '(lambda () (interactive) (kill-line 0)))
;; undoキーを追加(C-zを2回続けて入力)
(define-key my-keys-prefix-map (kbd "C-z") 'undo)
;; 日付の挿入(2025-12-22 mon 11:37 という形式)
(defun insert-current-time()
(interactive)
(insert (format-time-string "%Y-%m-%d %a %H:%M" (current-time))))
(define-key my-keys-prefix-map (kbd "C-d") 'insert-current-time)
;; denoteタイプの日付を挿入(20251222T114027 という形式)
(defun insert-denote-time()
"Insert denote-type datetime"
(interactive)
(insert (format-time-string "%Y%m%dT%H%M%S" (current-time))))
(define-key my-keys-prefix-map-bis (kbd "C-d") 'insert-denote-time)
;; 絵文字の挿入
(define-key my-keys-prefix-map (kbd "C-e") 'emoji-insert)
;; kill-sexp (C-M-k: S式を削除)を追加
(define-key my-keys-prefix-map (kbd "k") 'kill-sexp)
;; 現在のバッファを消去
(define-key my-keys-prefix-map (kbd "x") 'kill-current-buffer)
;; カーソル位置からバッファの終端まで削除
(defun my-kill-from-point-to-end ()
"Kill text from the current point to the end of the buffer."
(interactive)
(kill-region (point) (point-max))
(message "Killed from point to end of buffer"))
(global-set-key (kbd "C-c k") 'my-kill-from-point-to-end)
;; カーソル位置から先頭まで削除
(defun my-kill-from-point-to-beginning ()
"Kill text from the current point to the beginning of the buffer."
(interactive)
(kill-region (point) (point-min))
(message "Killed from point to beginning of buffer"))
(global-set-key (kbd "C-c K") 'my-kill-from-point-to-beginning)
;; 行末の空白文字を削除
(global-set-key (kbd "C-c t") 'delete-trailing-whitespace)
;; カーソルの位置の行をコピー
(defun copy-whole-line ()
"Copy the current entire line to the kill-ring."
(interactive)
(kill-ring-save (line-beginning-position) (line-end-position))
(message "Line copied"))
(global-set-key (kbd "C-c w") 'copy-whole-line)
;; 入力キーをそのまま挿入
(define-key global-map (kbd "C-c q") #'quoted-insert)
;; F1をヘルプのプレフィックスキーとして設定
(define-key global-map (kbd "<f1>") 'help-command)
;; ** F12で自分用のヘルプドキュメントを読み込み専用で開く
(defun my-open-help-emacs-md ()
"Open emacs help."
(interactive)
(find-file-read-only "~/Documents/DB/Help/emacs.md"))
(defun my-open-help-emacs-org ()
"Open org-mode help."
(interactive)
(find-file-read-only "~/Documents/DB/Help/emacs_org.md"))
(defun my-open-help-emacs-pdf ()
"Open emacs-pdf help."
(interactive)
(find-file-read-only "~/Documents/DB/Help/emacs_pdf.md"))
(defun my-open-help-emacs-regex ()
"Open emacs-regex help."
(interactive)
(find-file-read-only "~/Documents/DB/Help/emacs_regex.md"))
(global-set-key (kbd "<f12> e") #'my-open-help-emacs-md)
(global-set-key (kbd "<f12> o") #'my-open-help-emacs-org)
(global-set-key (kbd "<f12> p") #'my-open-help-emacs-pdf)
(global-set-key (kbd "<f12> r") #'my-open-help-emacs-regex)
;; 選択範囲で一時バッファを生成
(defun my-create-temp-buffer-from-region (start end)
"Create a temporary, unsaved buffer with the content of the selected region."
(interactive "r")
(let* ((region-text (buffer-substring-no-properties start end))
(temp-buffer (generate-new-buffer "*temp-region*")))
(switch-to-buffer temp-buffer)
(insert region-text)
(message "Copied region to temporary buffer: %s" (buffer-name temp-buffer))))
(global-set-key (kbd "C-z y") 'my-create-temp-buffer-from-region)
;; Ctrl+PageDownで次のタブへ移動
(global-set-key (kbd "C-<next>") #'tab-next)
;; Ctrl+PageUpで前のタブへ移動
(global-set-key (kbd "C-<prior>") #'tab-previous)
;; クエリ置換のキーを追加(M-%が打ちにくいので)
(define-key my-keys-prefix-map (kbd "C-r") 'query-replace)
;; 現在のバッファを完全削除
(defun my-delete-current-buffer-file ()
"現在のバッファに関連付けられたファイルを削除し、バッファを閉じます。"
(interactive)
(let ((filename (buffer-file-name)))
(if (and filename (file-exists-p filename))
(when (or (not (buffer-modified-p))
(yes-or-no-p "未保存の変更があります。変更を破棄して削除処理を進めますか? "))
(when (yes-or-no-p (format "本当にファイルを削除しますか? : %s " filename))
(delete-file filename)
(kill-buffer (current-buffer))
(message "ファイルを削除し、バッファを閉じました。")))
(message "このバッファはファイルに関連付けられていないか、ファイルが存在しません。"))))
(global-set-key (kbd "C-z C-k") 'my-delete-current-buffer-file)
6. 補完系パッケージ
-
consult + corfu + vertico + marginalia + orderless + embark + recentf + capeという組み合わせです - 日本語検索の補完のために
migemoを導入しました - 各パッケージとも最小限の設定にとどめています
;; == vertico ==
(use-package vertico
:ensure t
:config
(setq vertico-cycle t)
(setq vertico-resize nil)
(vertico-mode 1)
(savehist-mode 1)
(setq vertico-count 20))
;; == marginalia ==
(use-package marginalia
:ensure t
:config
(marginalia-mode 1))
;; == embark ==
(use-package embark
:ensure t
:bind
(("M-." . embark-act)
("C-c e" . embark-dwim)
("C-c E" . embark-bindings))
:init
(setq prefix-help-command #'embark-prefix-help-command)
:config
(add-to-list 'display-buffer-alist
'("\\`\\*Embark Collect \\(Live\\|Completions\\)\\*"
nil
(window-parameters (mode-line-format . none)))))
;; == embark-consult ==
(use-package embark-consult
:ensure t ; only need to install it, embark loads it after consult if found
:hook
(embark-collect-mode . consult-preview-at-point-mode))
;; == migemo ==
(use-package migemo
:ensure t
:init
(setopt migemo-command "cmigemo")
(setopt migemo-options '("-q" "--emacs"))
;; Migemo辞書のパス設定
(setopt migemo-dictionary
"/usr/share/migemo/utf-8/migemo-dict")
(setopt migemo-user-dictionary nil) ; ユーザー辞書は使用しない
(setopt migemo-regex-dictionary nil) ; 正規表現辞書は使用しない
(setopt migemo-coding-system 'utf-8-unix) ; 辞書のエンコーディング
:config
(migemo-init))
;; == orderless ==
(use-package orderless
:ensure t
:config
(defun orderless-migemo (component)
(let ((pattern (migemo-get-pattern component)))
(condition-case nil
(progn (string-match-p pattern "") pattern)
(invalid-regexp nil))))
(setq orderless-matching-styles
'(orderless-literal ; そのままの文字列
orderless-regexp ; 正規表現
orderless-migemo)) ; Migemo変換
(setq completion-styles '(orderless basic)
completion-category-defaults nil
completion-category-overrides '((file (styles partial-completion)))))
;; == consult ==
(use-package consult
:ensure t
:demand t
:bind (("C-s" . my-consult-line)
([remap goto-line] . consult-goto-line)
("M-g o" . consult-outline)
("C-x b" . consult-buffer)
("M-y" . consult-yank-pop)
("M-g r" . consult-ripgrep)
("M-g f" . consult-fd)
("M-g i" . consult-imenu)
("M-g I" . consult-imenu-multi)
("M-g m" . consult-mark)
("M-g M" . consult-global-mark)
("M-g l" . consult-line))
:config
;; C-uを付けるとカーソル位置の文字列を使うmy-consult-lineコマンドを定義
(defun my-consult-line (&optional at-point)
"Consult-line uses things-at-point if set C-u prefix."
(interactive "P")
(if at-point
(consult-line (thing-at-point 'symbol))
(consult-line))))
;; == consult-eglot ==
(use-package consult-eglot
:ensure t
:after eglot
:hook (eglot-managed-mode . (lambda () (local-set-key (kbd "M-g s") #'consult-eglot-symbols))))
;; == consult-dir ==
(use-package consult-dir
:ensure t
:bind (("C-x C-d" . consult-dir)
:map minibuffer-local-completion-map
("C-x C-d" . consult-dir)
("C-x C-j" . consult-dir-jump-file)))
;; ** == recentf ==
(use-package recentf
:ensure t
:demand t
:config
(recentf-mode 1)
(setq recentf-save-file "~/.emacs.d/recentf")
(setq recentf-max-saved-items 3000)
(setq recentf-exclude '("recentf" "\\.epub\\'" "/home/hy/Commune/Calibre_2/.*"))
(setq recentf-auto-save-timer
(run-with-idle-timer 1200 t 'recentf-save-list)))
;; == corfu ==
(use-package corfu
:ensure t
:demand t
:config
(global-corfu-mode +1)
(setq corfu-cycle t)
(setq corfu-auto t)
(setq corfu-preselect 'prompt)
(setq corfu-quit-no-match 'separator)
(setq corfu-quit-at-boundary nil)
(setq corfu-scroll-margin 2)
(setq corfu-auto-delay 0.2)
;; ミニバッファでの補完を有効にする
(defun corfu-enable-always-in-minibuffer ()
"Enable Corfu in the minibuffer if Vertico/Mct are not active."
(unless (or (bound-and-true-p mct--active)
(bound-and-true-p vertico--input))
(corfu-mode 1)))
(add-hook 'minibuffer-setup-hook #'corfu-enable-always-in-minibuffer 1))
;; == corfu-terminal ==
(unless (display-graphic-p)
(corfu-terminal-mode +1))
;; == cape ==
(use-package cape
:ensure t
:init
(add-to-list 'completion-at-point-functions #'cape-dabbrev)
(add-to-list 'completion-at-point-functions #'cape-file)
(add-to-list 'completion-at-point-functions #'cape-keyword))
7. 各種パッケージの設定
- コーディングとライティングの双方で使われる著名なパッケージと、検索コマンド関連の設定です
;; === dired ===
(use-package dired
:ensure nil
:commands (dired)
:hook
((dired-mode . dired-hide-details-mode)
(dired-mode . hl-line-mode))
:config
(setq dired-recursive-copies 'always)
(setq dired-recursive-deletes 'always)
(setq delete-by-moving-to-trash t)
(setq dired-dwim-target t))
(use-package dired-subtree
:ensure t
:after dired
:bind
( :map dired-mode-map
("<tab>" . dired-subtree-toggle)
("TAB" . dired-subtree-toggle)
("<backtab>" . dired-subtree-remove)
("S-TAB" . dired-subtree-remove))
:config
(setq dired-subtree-use-backgrounds nil))
(use-package dired-filter
:ensure t
:config
(dired-filter-mode 1))
;; == apheleia(フォーマットの実行) ==
(use-package apheleia
:ensure t
:config
(apheleia-global-mode +1)
(setf (alist-get 'ruff-isort apheleia-formatters)
'("ruff" "check" "--select" "I" "--fix" "--stdin-filename" filepath "-"))
(setf (alist-get 'ruff-format apheleia-formatters)
'("ruff" "format" "--stdin-filename" filepath "-"))
(setf (alist-get 'python-ts-mode apheleia-mode-alist) '(ruff-isort ruff-format))
(setf (alist-get 'python-mode apheleia-mode-alist) '(ruff-isort ruff-format))
(setf (alist-get 'html-mode apheleia-mode-alist) '(prettier-html))
(setf (alist-get 'css-mode apheleia-mode-alist) '(prettier-css))
(setf (alist-get 'go-mode apheleia-mode-alist) '(goimports))
(setf (alist-get 'go-ts-mode apheleia-mode-alist) '(goimports)))
;; == vterm(関連ライブラリのインストールや事前コンパイルが必要) ==
(use-package vterm
:ensure t
:commands vterm
:bind (:map vterm-mode-map
("C-c C-y" . vterm-copy-mode)
("C-q" . vterm-send-next-key)
("C-c C-f" . find-file))
:config
(setq vterm-shell "zsh")
(setq vterm-max-scrollback 10000)
(setq vterm-keymap-exceptions
'("<f1>" "<f2>" "<f5>" "<f6>" "<f7>" "<f8>" "<f9>" "<f12>"
"C-x" "C-u" "C-g" "C-z" "C-t" "C-s" "C-v" "C-1"
"M-x" "M-o" "M-v" "M-y" "M-." "M-i" "C-y"))
(add-hook 'vterm-mode-hook
(lambda ()
(display-line-numbers-mode -1)
(hl-line-mode -1)
(setq-local truncate-lines t))))
;; vtermの終了時にmd形式のログファイルを自動保存する
(add-hook 'vterm-exit-functions
(lambda (buffer _event)
(with-current-buffer buffer
(let ((filename (format "~/Documents/MyAgentLogs/vterm-%s.md" (format-time-string "%Y%m%d-%H%M%S"))))
(write-region (point-min) (point-max) filename)
(message "Vterm log saved to %s" filename)))))
;; == magit ==
(use-package magit
:ensure t
:defer t
:bind (("C-x g" . magit-status))
:config
(defun mu-magit-kill-buffers ()
"Restore window configuration and kill all Magit buffers."
(interactive)
(let ((buffers (magit-mode-get-buffers)))
(magit-restore-window-configuration)
(mapc #'kill-buffer buffers)))
(bind-key "q" #'mu-magit-kill-buffers magit-status-mode-map))
;; == minions(マイナーモードの表示を簡素化) ==
(use-package minions
:ensure t
:demand t
:bind (:map my-keys-prefix-map
("m" . minions-minor-modes-menu))
:config
(minions-mode 1)
(setq minions-mode-line-lighter "[+]"))
;; == expand-region(C-c < で選択範囲を拡大) ==
(use-package expand-region
:ensure t)
(global-set-key (kbd "C-c <") 'er/expand-region)
;; == symbol-overlay ==
(use-package symbol-overlay
:ensure t)
(add-hook 'prog-mode-hook #'symbol-overlay-mode)
(add-hook 'markdown-mode-hook #'symbol-overlay-mode)
(global-set-key (kbd "M-i") 'symbol-overlay-put)
(define-key symbol-overlay-map (kbd "p") 'symbol-overlay-jump-prev) ; 前のシンボルへ
(define-key symbol-overlay-map (kbd "n") 'symbol-overlay-jump-next) ; 次のシンボルへ
(define-key symbol-overlay-map (kbd "C-g") 'symbol-overlay-remove-all) ; ハイライトキャンセル
;; == vundo ==
(use-package vundo
:ensure t)
(global-set-key (kbd "C-?") 'vundo)
(define-key my-keys-prefix-map (kbd "u") 'vundo)
;; ** yasnippet
(use-package yasnippet
:ensure t
:defer t
:init
(add-hook 'prog-mode-hook #'yas-minor-mode)
(add-hook 'text-mode-hook #'yas-minor-mode)
:config
(setq yas-snippet-dirs '("~/.emacs.d/mysnippets"))
:bind
("C-z C-p" . yas-insert-snippet))
;; == eww ==
(setq eww-search-prefix "https://duckduckgo.com/?q=%s")
(setq url-user-agent "Mozilla/5.0 (X11; Linux x86_64; rv:126.0) Gecko/20100101 Firefox/126.0")
(setq shr-width 80)
(setq eww-image-max-width 600)
;; == ripgrep ==
(setq ripgrep-executable "/usr/bin/rg")
(setq ripgrep-arguments '("-S")) ; rgに渡すオプション
(setq xref-search-program 'ripgrep) ; 検索をripgrepに設定
(global-set-key (kbd "C-c r g") 'ripgrep-regexp)
;; == rg ==
(use-package rg
:ensure t
:config
(define-key global-map (kbd "C-c r r") #'rg-project)
(define-key global-map (kbd "C-c r m") #'rg-menu))
;; == wgrep ==
(use-package wgrep
:ensure t
:demand t)
;; == hunspell(hunspellと辞書のインストールが必要) ==
(setq-default ispell-program-name "hunspell")
(setq ispell-dictionary "en_US")
(with-eval-after-load "ispell"
(setq ispell-hunspell-dictionary-alist
'(("en_US" "[[:alpha:]]" "[^[:alpha:]]" "[']" nil ("-d" "en_US") nil utf-8)))
(setq ispell-local-dictionary "en_US")
(add-to-list 'ispell-skip-region-alist '("[^\000-\377]+")))
;; テキストモードで補完候補を表示しない
(defun my-disable-ispell-in-text-modes ()
"Remove ispell completion function in text-based modes."
(setq-local completion-at-point-functions
(remq 'ispell-completion-at-point
(copy-sequence completion-at-point-functions))))
(add-hook 'text-mode-hook 'my-disable-ispell-in-text-modes)
;; == multiple-cursors(複数カーソル) ==
(use-package multiple-cursors
:ensure t
:bind (:map my-keys-prefix-map
("." . mc/edit-lines)
(">" . mc/mark-next-like-this)
("<" . mc/mark-previous-like-this)
("," . mc/mark-all-like-this)))
;; == smartparens(コーディング時に対応するカッコを挿入) ==
(use-package smartparens
:ensure t
:hook (prog-mode . smartparens-mode)
:bind ("C-\\" . smartparens-mode))
;; == iedit(複数個所を同時編集) ==
(use-package iedit
:ensure t
:demand t)
;; == quickrun(ターミナルを経由せずにコードを実行) ==
(use-package quickrun
:ensure t
:bind (:map my-keys-prefix-map
("c" . quickrun)))
;; == direnv(ディレクトリの環境変数を自動認識)==
(use-package direnv
:ensure t
:config
(direnv-mode))
;; == ace-window(ウィンドウの賢い切り替え) ==
(use-package ace-window
:ensure t
:defer t
:bind
("M-o" . ace-window))
;; == avy(キー操作で選択 ➔ アクション) ==
(use-package avy
:ensure t
:config (setq avy-style 'pre)
:bind (("C-c h" . avy-goto-char-timer)
("C-c H" . avy-goto-line)))
8. コーディング関連
- 本稿の趣旨とずれるため、詳細には触れません
- この項目の設定は、各言語のエキスパート諸氏の設定を参照してください
;; == eglot ==
(use-package eglot
:ensure t
:defer t
:init
(setq eglot-events-buffer-size 0)
:config
(setq eglot-ignored-server-capabilities
'(:documentHighlightProvider
:workspace/didChangeWorkspaceFolders)))
;; == eglot-booster ==
(use-package eglot-booster
:ensure t
:after eglot
:config
(eglot-booster-mode))
;; == tree-siter ==
(use-package treesit
:ensure nil
:config
(setq treesit-font-lock-level 4)
(setq treesit-language-source-alist
'((typescript "https://github.com/tree-sitter/tree-sitter-typescript" "master" "typescript/src")
(tsx "https://github.com/tree-sitter/tree-sitter-typescript" "master" "tsx/src"))))
;; == LANGUAGE SETTINGS ==
;; Python
(setq auto-mode-alist (cons '("\\.py$" . python-ts-mode) auto-mode-alist))
(use-package python-ts-mode
:ensure nil
:hook
((python-ts-mode . eglot-ensure)
(python-ts-mode . (lambda () (setq-local python-indent-offset 4))))
:config
(add-to-list 'eglot-server-programs
'(python-ts-mode "pyright-langserver")))
;; pet
(use-package pet
:ensure t
:defer t
:config
(add-hook 'python-ts-mode-hook 'pet-mode -10))
;; Clang
(use-package c-ts-mode
:ensure nil
:hook
(c-ts-mode . eglot-ensure)
:if (treesit-language-available-p 'c)
:custom
(c-ts-mode--indent-styles 'cpp)
:init
;; Remap the standard C/C++ modes
(add-to-list 'major-mode-remap-alist '(c-mode . c-ts-mode))
(add-to-list 'major-mode-remap-alist '(c++-mode . c++-ts-mode))
(add-to-list 'major-mode-remap-alist '(c-or-c++-mode . c-or-c++-ts-mode)))
;; Rust
(use-package rust-ts-mode
:ensure nil
:mode ("\\.rs\\'" . rust-mode)
:hook
(rust-ts-mode . eglot-ensure))
;; Javascript
(use-package js-ts-mode
:ensure nil
:mode ("\\.js\\'" . js-ts-mode)
:hook
(js-ts-mode . eglot-ensure))
;; Typescript
(use-package typescript-ts-mode
:ensure t
:mode ("\\.ts\\'" . typescript-ts-mode)
:hook (typescript-ts-mode . eglot-ensure))
;; Shellscript
(use-package sh-base-mode
:ensure nil
:hook
(sh-base-mode . eglot-ensure))
;; Sly/Clisp
(use-package sly
:ensure t
:hook ((lisp-mode . font-lock-mode)
(sly-mode . font-lock-mode))
:config
(customize-set-variable
'inferior-lisp-program
"sbcl --noinform --no-sysinit"))
;; Scheme
(use-package geiser
:ensure nil
:mode (("\\.scm\\'" . scheme-mode)
("\\.ss\\'" . scheme-mode))
:hook (geiser-mode . (lambda ()
(eldoc-mode 1)
(corfu-mode 1)
(flymake-mode 1)))
:config
(setq geiser-active-implementations '(guile))
(setq geiser-repl-print-level 3)
(setq geiser-repl-print-length 3)
(require 'geiser-guile))
;; Golang
(use-package go-mode
:ensure t
:mode "\\.go\\'"
:config
(add-hook 'go-mode-hook #'eglot-ensure))
9. 翻訳
- 日本語と英語、イタリア語の翻訳のための設定です
9.1 Google翻訳
(use-package gt
:ensure t
:bind ("C-z t" . gt-translate)
:config
(setq gt-langs '(ja en it))
(setq gt-default-translator
(gt-translator
:taker (gt-taker :text 'buffer :pick 'paragraph)
:engines (list
(gt-google-engine)
)
:render (gt-buffer-render))))
(defun my-focus-gt-result-buffer-after-translate (&rest _)
"翻訳後に*gt-result*バッファが表示されているウィンドウにフォーカスを移す。"
(let ((result-window (get-buffer-window "*gt-result*" 'visible)))
(when result-window
(select-window result-window))))
(advice-add 'gt-translate :after #'my-focus-gt-result-buffer-after-translate)
9.2 deepl-cli.el (DeepL翻訳)
- pythonのdeeplパッケージを導入してください
- 例:
uv tool install deepl
- 例:
- 下記の
deepl-cli.elをadd-to-list 'load-pathで指定したディレクトリに保存してください
;;; deepl-cli.el --- DeepL CLI integration -*- lexical-binding: t; -*--
(require 'cl-lib)
;; APIキー設定用の変数(未定義の場合はnil)
(defvar deepl-auth-key nil)
(defvar deepl-confirmation-threshold 3000)
;; 長文送信時の確認
(cl-defun confirm-send-long-string (&key retry)
(let ((send-it-p
(read-from-minibuffer
(if retry
"Please answer with \"yes\" or \"no\". [yes/no]: "
(format "It's over %S characters, do you really want to send it? [yes/no]: "
deepl-confirmation-threshold)))))
(cond ((equal send-it-p "yes") t)
((equal send-it-p "no") nil)
(t (confirm-send-long-string :retry t)))))
;; 翻訳結果を処理する関数
(defun deepl--handle-result (translated-text)
(kill-new translated-text)
(message "%s" translated-text))
;; 内部関数: deeplコマンドを呼び出す
(cl-defun deepl-translate-internal-cli (text source-lang target-lang callback)
"deeplコマンドを使って翻訳を行います。"
;; キーのチェック
(unless (and (boundp 'deepl-auth-key) deepl-auth-key)
(user-error "deepl-auth-key is not set. Please set it in your config: (setq deepl-auth-key \"YOUR_KEY_HERE\")"))
;; 文字数チェック
(when (and (> (length text) deepl-confirmation-threshold)
(not (confirm-send-long-string)))
(cl-return-from deepl-translate-internal-cli))
;; プロセス作成と実行
(let* ((auth-env (format "DEEPL_AUTH_KEY=%s" deepl-auth-key))
(process-environment (cons auth-env process-environment))
(out-buf (generate-new-buffer " *deepl-output*"))
(proc (make-process
:name "deepl-cli"
:buffer out-buf
:command (list "deepl" "text" "--to" target-lang "--from" source-lang "-")
:coding 'utf-8
:connection-type 'pipe
:sentinel (lambda (proc event)
(when (memq (process-status proc) '(exit signal))
(let ((exit-code (process-exit-status proc)))
(if (zerop exit-code)
;; 成功時
(with-current-buffer (process-buffer proc)
(let ((result (string-trim (buffer-string))))
(if (string-empty-p result)
(message "DeepL CLI returned empty result.")
(funcall callback result)))
(kill-buffer (process-buffer proc)))
;; エラー時
(progn
(message "DeepL CLI Error (Code %d). Buffer content: %s"
exit-code
(with-current-buffer (process-buffer proc) (buffer-string)))
(kill-buffer (process-buffer proc))))))))))
;; 標準入力へテキストを送信
(if (process-live-p proc)
(progn
(process-send-string proc text)
(process-send-eof proc))
(message "Failed to create DeepL process"))))
;; --- イタリア語翻訳用関数 ---
(defun deepl-ij (start end)
"イタリア語から日本語に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(deepl-translate-internal-cli region "IT" "JA" #'deepl--handle-result)))
(defun deepl-ji (start end)
"日本語からイタリア語に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(deepl-translate-internal-cli region "JA" "IT" #'deepl--handle-result)))
(defun deepl-translate2 (start end)
"日本語とイタリア語を自動判別して相互に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(if (ja-string-p region)
(deepl-translate-internal-cli region "JA" "IT" #'deepl--handle-result)
(deepl-translate-internal-cli region "IT" "JA" #'deepl--handle-result))))
;; --- 英語翻訳用関数 ---
(defun deepl-ej (start end)
"英語から日本語に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(deepl-translate-internal-cli region "EN" "JA" #'deepl--handle-result)))
(defun deepl-je (start end)
"日本語から英語に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(deepl-translate-internal-cli region "JA" "EN-US" #'deepl--handle-result)))
(defun deepl-translate1 (start end)
"日本語と英語を自動判別して相互に翻訳します。"
(interactive "r")
(let ((region (buffer-substring-no-properties start end)))
(if (ja-string-p region)
(deepl-translate-internal-cli region "JA" "EN-US" #'deepl--handle-result)
(deepl-translate-internal-cli region "EN" "JA" #'deepl--handle-result))))
;; 共通ヘルパー関数
(defun ja-char-p (char)
(or (<= #x3041 char #x309f) ; hiragana
(<= #x30a1 char #x30ff) ; katakana
(<= #x4e01 char #x9faf) ; kanji
))
(defun ja-string-p (str)
(>= (cl-count-if #'ja-char-p str) 3))
(provide 'deepl-cli)
init.elには次のように記述します。
(use-package deepl-cli
:ensure nil
:bind (:map my-keys-prefix-map
("e" . deepl-translate1) ; 日英
("i" . deepl-translate2)) ; 日伊
:init
(setq deepl-auth-key "ご自身のAPIキー"))
10. 電子辞書
-
sdcvとgoldendictがスタンドアローンで動作することを確認してください - 辞書のリストはご自身の環境と合わせてください
;; == quick sdcv(辞書検索の結果を別バッファに表示)==
(use-package quick-sdcv
:ensure t
:custom
(quick-sdcv-dictionary-prefix-symbol "►")
(quick-sdcv-ellipsis " ▼ ")
:config
(define-key quick-sdcv-mode-map (kbd "q") 'quit-window))
(setq quick-sdcv-dictionary-complete-list
'("EIJIRO"
"新明解国語辞典"
"広辞苑"
"American Heritage Dictionary 4th Ed. (En-En)"
"Oxford English Dictionary 2nd Ed. P1"
"Oxford English Dictionary 2nd Ed. P2"
))
(define-key my-keys-prefix-map (kbd "s") 'quick-sdcv-search-at-point) ;; カーソル位置の単語検索
(define-key my-keys-prefix-map (kbd "S") 'quick-sdcv-search-input) ;; キーボード入力で単語検索
;; == goldendict(辞書検索の結果を外部アプリのポップアップ表示) ==
;; goldendictのscan-popupオプションをONにしておくこと
(defun look-up-dict (word)
(start-process "goldendict" nil "goldendict" word))
(defun look-up-dict-marked ()
(interactive)
(let ((word ""))
(if (equal major-mode 'pdf-view-mode)
(setq word (car (pdf-view-active-region-text)))
(setq word (buffer-substring (mark) (point))))
(look-up-dict word)))
(define-key my-keys-prefix-map (kbd "g") 'look-up-dict-marked)
;; == websearch(ブラウザ経由でGoogle検索) ==
(use-package emacs-websearch
:ensure nil
:bind (("C-z h" . emacs-websearch))
:config
(setq emacs-websearch-engine 'google)
(setq emacs-websearch-async t))
11. LLM関連の設定
-
gemini-cliに関連した設定だけを書きました - emacsとAIコーディングエージェントの統合については、まだ納得できる環境が構築できていません
-
gemini-cliとgmn(gemini-cliよりスタートアップが37倍高速)の事前インストールが必要です。
11.1 emacsからgemini-cliを呼び出す
選択したリージョンをgemini-cli経由で質問すると、結果をmarkdown形式で表示してくれる関数です。
;; == Gemini CLI ==
(defun my-gemini-cli-chat-region (start end)
"リージョンをGeminiに質問し、結果をMarkdown形式で表示する"
(interactive "r")
(let* ((question (buffer-substring-no-properties start end))
(command (concat "gmn -p " (shell-quote-argument question)))
(output-buffer-name "*Gemini Chat Output*")
(buf (get-buffer-create output-buffer-name)))
(message "Geminiに問い合わせ中...")
(with-current-buffer buf
(setq buffer-read-only nil)
(erase-buffer)
;; Markdownモードを適用
(when (fboundp 'markdown-mode)
(markdown-mode))
;; ANSIカラーを有効にする設定
(setq-local ansi-color-for-comint-mode t))
(let ((proc (start-process-shell-command "gemini-process" buf command)))
(set-process-sentinel proc
(lambda (p e)
(when (string= e "finished\n")
(message "Geminiからの回答が完了しました。")
))))
(display-buffer buf)))
(global-set-key (kbd "C-z C-a") 'my-gemini-cli-chat-region)
11.2 チャットの内容をorg-mode形式へ変換する
gemini-cliには/copyコマンドでチャット内容をmarkdown形式でクリップボードに送信してくれる機能があります。これをpandoc経由でogr-modeのファイル形式に変換し、貼り付ける関数です。
;;; === Gemini CLI to Org ===
(defun my-paste-gemini-chat-as-org ()
"Get Gemini chat log from clipboard (Markdown), convert it to Org format, and insert."
(interactive)
(let* ((md-text (current-kill 0))
(pandoc-command "pandoc")
(org-text (shell-command-to-string
(format "echo %s | %s -f markdown -t org"
(shell-quote-argument md-text)
pandoc-command))))
(with-temp-buffer
(insert org-text)
(goto-char (point-min))
(while (re-search-forward "^#\\+BEGIN_QUOTE\n\\(.*?\\)\n#\\+END_QUOTE" nil t)
(replace-match "* \\1" t t))
(goto-char (point-min))
(while (re-search-forward "^----*$" nil t)
(replace-match "" t t))
(goto-char (point-min))
(while (re-search-forward "\n\n\n+" nil t)
(replace-match "\n\n" t t))
(kill-ring-save (point-min) (point-max)))
(yank)
(message "Gemini chat log converted to Org format and pasted.")))
(global-set-key (kbd "C-z C-y") 'my-paste-gemini-chat-as-org)
11.3 LLM Agentのログを検索
geminiやclaudeとのチャット内容は、すべて~/Documents/MyAgentLogs/に保存しています。大量のログファイルの中から、目的のキーワードを素早く検索するための関数を定義しました。
;; == LLM Agentのログを検索 ==
(defun my-consult-ripgrep-agent-logs ()
"Search in MyAgentLogs directory using `consult-ripgrep' with Japanese support."
(interactive)
(let* ((consult-ripgrep-args (concat consult-ripgrep-args " -a --encoding utf-8"))
(coding-system-for-read 'utf-8)
(coding-system-for-write 'utf-8))
(consult-ripgrep (expand-file-name "~/Documents/MyAgentLogs/"))))
(global-set-key (kbd "C-z a") 'my-consult-ripgrep-agent-logs)
11.4 agent-shellの導入
emacs内とllmエージェントとの統合のためにagent-shellを導入しました。
- ターミナル上の
gemini-cliと一部キーバインディングが異なる - Gemini 3.1 proの登場後、特に動作が不安定になった
という理由で、最近はあまり使っていません。日本語入力の不便さに目をつぶれば、vterm上でgemini-cliを起動した方が快適です(vtermを閉じたとき、ログを自動保存する設定をしています)。
;; == agent-shell ==
(use-package agent-shell
:ensure t
:vc (:url "https://github.com/google/gemini-cli"
:lisp-dir "emacs/agent-shell")
:bind (("C-z C-g" . agent-shell-google-start-gemini))
:config
(setq agent-shell-transcript-file-path-function
(lambda ()
(let* ((log-dir "~/Documents/MyAgentLogs/")
(filename (format-time-string "%Y%m%dT%H%M%S--agent-log__llm.md"))
(filepath (expand-file-name filename log-dir)))
filepath))))
11.5 選択テキストをQRコードに変換して表示
Linuxからスマホへ:ネットワークを介さない「物理層」コピペを参照してください。
;; == QR Code Generation ==
(defun my-region-to-qrcode (start end)
"選択範囲を一時ファイル経由でQRコードに変換して表示(nohup版)"
(interactive "r")
(let ((text-file (make-temp-file "qr-text" nil ".txt"))
(img-file (make-temp-file "qr-image" nil ".png"))
(exit-code 0))
(write-region start end text-file)
(setq exit-code
(call-process "qrencode" nil nil nil "-s" "10" "-r" text-file "-o" img-file))
(if (and (= exit-code 0) (file-exists-p img-file))
(progn
(message "QRコードを表示: %s" img-file)
(call-process-shell-command
(format "nohup xdg-open %s >/dev/null 2>&1 &" img-file)
nil 0))
(message "QRコード生成失敗 (Exit code: %d)" exit-code))))
(define-key my-keys-prefix-map (kbd "q") 'my-region-to-qrcode)
12. org-mode
- org-modeの設定は膨大かつ十人十色なので、設定だけ書くことにします(いつか自分のワークフローを交えて、org-modeに関する記事を書きたいと思います)
- denoteを核としたプレーンなテキスト環境です
-
rgやfdといった高速な検索コマンドとの連携で(わたくしの仕事の範囲では)快適に動作しています
12.1 org-mode関連のファイル置き場
;; file directory
(setq org-directory "~/Documents/MyOrg")
(setq org-default-notes-file "20250401T010000--mynotes__memo_private.org")
;; org-captureのキーシーケンス
(global-set-key (kbd "C-c c") 'org-capture)
;; org-captureのテンプレート(メニュー)の設定
(setq org-capture-templates
'(
("n" "Note" entry (file+headline "~/Documents/MyOrg/notes/20250401T010000--mynotes__memo_private.org" "Notes") "* %?\nEntered on %U\n %i\n %a")
("t" "Todo" entry (file+headline "~/Documents/MyOrg/notes/20250401T030000--mytask__private.org" "Capture") "** TODO %? \n SCHEDULED: %^t \n")
("w" "Work" entry (file+headline "~/Documents/MyOrg/notes/20250401T040000--mywork__work.org" "Work") "* %?\nEntered on %U")
("v" "Voca" entry (file+headline "~/Documents/MyOrg/notes/20250325T000000--english-glossary__database.org" "2025 (voca)")"* %?")
("a" "AI" entry (file+headline "~/Documents/MyOrg/notes/20250401T050000--myai__ai_llm.org" "Q_board") "* %?\nEntered on %U\n %i\n %a")
("b" "Bulk" entry (file+headline "~/Documents/MyOrg/notes/20250825T184025--mybulk-news-link__actuality_portal.org" "misc") "* %?\nEntered on %U\n %i\n %a")
))
;; mynotes.orgをC-c nで参照
(defun show-org-buffer (file)
"Show my org-file FILE on the current buffer."
(interactive)
(if (get-buffer file)
(let ((buffer (get-buffer file)))
(switch-to-buffer buffer)
(message "%s" file))
(find-file (concat "~/Documents/MyOrg/notes/" file))))
(global-set-key (kbd "C-c n") #'(lambda () (interactive)
(show-org-buffer "20250401T010000--mynotes__memo_private.org")))
12.2 表示関連
;; org-modeで行を折り返さない
(setq org-startup-truncated nil)
;; 階層に合わせて左にインデント
(setq org-startup-indented t)
;; 見出しをインデントした時にアスタリスクが減るのを防ぐ
(setq org-indent-mode-turns-on-hiding-stars nil)
;; インデントの幅を設定
(setq org-indent-indentation-per-level 2)
;; 見出しの初期状態
(setq org-startup-folded t)
(setq org-startup-with-inline-images t)
;; 表示設定(プロポーショナルフォント表示)
(add-hook 'org-mode-hook (lambda () (setq line-spacing 0.1)))
(with-eval-after-load 'org
;; Header scaling and colors
(set-face-attribute 'org-level-1 nil :inherit 'variable-pitch :height 1.1 :weight 'bold :foreground "#62b2ff")
(set-face-attribute 'org-level-2 nil :inherit 'variable-pitch :height 1.1 :weight 'bold :foreground "#19e37d")
(set-face-attribute 'org-level-3 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#f0ce43")
(set-face-attribute 'org-level-4 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#ff79c6")
(set-face-attribute 'org-level-5 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#bd93f9")
(set-face-attribute 'org-level-6 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#46d9ff")
;; Ensure structural elements use fixed-pitch font
(set-face-attribute 'org-block nil :inherit 'fixed-pitch)
(set-face-attribute 'org-code nil :inherit 'fixed-pitch)
(set-face-attribute 'org-table nil :inherit 'fixed-pitch)
(set-face-attribute 'org-verbatim nil :inherit 'fixed-pitch)
(set-face-attribute 'org-formula nil :inherit 'fixed-pitch)
(set-face-attribute 'org-special-keyword nil :inherit 'fixed-pitch)
(set-face-attribute 'org-meta-line nil :inherit 'fixed-pitch)
(set-face-attribute 'org-checkbox nil :inherit 'fixed-pitch)
;; Quote style matching markdown-mode
(set-face-attribute 'org-quote nil :inherit 'variable-pitch :slant 'italic))
;; 画像のサイズ
(setq org-image-actual-width 300)
;; サブツリー内の画像の表示を制御
(defun org-toggle-inline-images-in-subtree ()
"Show images in the subtree only."
(interactive)
(save-excursion
(save-restriction
(org-narrow-to-subtree)
(org-toggle-inline-images)
(widen))))
(global-set-key (kbd "C-c v") 'org-toggle-inline-images-in-subtree)
;; ビュレット
(use-package org-bullets
:ensure t
:config (setq org-bullets-bullet-list '("∑" "⨕" "∫" "Ω" "☿" "∅" "∢" "⚕"))
:hook (org-mode . org-bullets-mode))
;; Emphaisis markers(*、/、=など)を非表示にする
(setq org-hide-emphasis-markers t)
12.3 リンク関連
;; org-store-linkのキーシーケンス
(global-set-key (kbd "C-c l") 'org-store-link)
(global-set-key (kbd "<f8>") 'org-store-link)
;; org-insert-linkのキーシーケンス
(global-set-key (kbd "C-c C-l") 'org-insert-link)
(global-set-key (kbd "<f9>") 'org-insert-link)
;; リンク表示(トグル)
(global-set-key (kbd "C-c f") 'org-toggle-link-display)
;; カーソル位置のリンクをミニバッファに表示する
(with-eval-after-load 'org
(defun my-org-display-raw-link-at-point ()
"Display the raw link when the cursor is on an Org mode link.
This command is intended to be called interactively."
(interactive)
;; org-agenda-mode で org-element-context を使用すると警告が出ることがあるため抑制
(let ((element (let ((warning-minimum-level :error))
(org-element-context))))
;; カーソル位置の要素がリンクであるかを確認
(if (and element (eq (car element) 'link))
;; リンクの場合、*Messages* バッファに記録せずにミニバッファ(echo area)に表示
(let ((message-log-max nil))
(message "%s" (propertize (org-element-property :raw-link element) 'face 'org-link)))
;; リンクでない場合、ユーザーに通知
(message "No link at point"))))
;; org-mode で "C-z t" にコマンドを割り当て
(define-key org-mode-map (kbd "C-t v") #'my-org-display-raw-link-at-point)
;; 元のコードと同様に org-agenda-mode でも利用可能にする
(define-key org-agenda-mode-map (kbd "C-t v") #'my-org-display-raw-link-at-point))
;; Org-download
(use-package org-download
:ensure t
:demand t)
(setq-default org-download-image-dir "~/Documents/MyOrg/metadata")
(add-hook 'dired-mode-hook 'org-download-enable) ; Drag-and-drop to `dired`
(define-key org-mode-map (kbd "C-c L") 'org-download-yank)
12.4 機能設定
;; org-sparse-treeを別バッファで表示
(defun org-sparse-tree-indirect-buffer (arg)
(interactive "P")
(let ((ibuf (switch-to-buffer (org-get-indirect-buffer))))
(condition-case _
(org-sparse-tree arg)
(quit (kill-buffer ibuf)))))
(define-key org-mode-map (kbd "C-c /") 'org-sparse-tree-indirect-buffer)
;; org-backward-heading-same-level
(define-key org-mode-map (kbd "C-z z") 'org-backward-heading-same-level)
;; org-tempoを有効化
(use-package org-tempo
:ensure nil
:after org)
;; Speed-commandsをONにする
(setq org-use-speed-commands t)
;; outline-hide-by-heading-regexpのキーを変更
(define-key org-mode-map (kbd "C-c s") 'outline-hide-by-heading-regexp)
(define-key org-mode-map (kbd "C-c S") 'outline-show-by-heading-regexp)
;; org-babel(ほとんど使っていない)
(org-babel-do-load-languages
'org-babel-load-languages
'((C . t)
(emacs-lisp . t)
(python . t)
(shell . t)))
;; org-modeのheadingで日本語入力をオフ
(defun my-org-disable-ime-on-heading ()
"When the cursor is on a heading's asterisk in Org mode, disable the input method."
(when (and (eq major-mode 'org-mode)
(bolp)
(looking-at "\\*+"))
(when current-input-method
(deactivate-input-method))))
(add-hook 'post-command-hook #'my-org-disable-ime-on-heading)
;; org-qlでアジェンダファイルを高速検索
(use-package org-ql
:ensure t
:after (org consult)
:bind (("C-t q" . org-ql-find)
("C-t Q" . org-ql-search))
:config
(setq org-ql-find-source (org-agenda-files)))
12.5 ファイル変換とエクスポート
;; 選択領域をmarkdownからorg形式へ
(defun my-md-to-org-region (start end)
"Convert region from markdown to org, replacing selection"
(interactive "r")
(shell-command-on-region start end "pandoc -f markdown -t org" t t))
(global-set-key (kbd "C-c m") 'my-md-to-org-region)
;; markdownへの変換パッケージの呼び出し
(use-package ox-md
:ensure nil
:after org)
;; 選択範囲をorgからmdに変換してkill
(use-package org-to-markdown-and-kill
:ensure nil
:bind ("C-c p" . org-to-markdown-and-kill))
;; pandocでエクスポート
(use-package ox-pandoc
:ensure t
:after org
:config
;; docx 出力時に特定のテンプレートを使う場合など
;; (setq org-pandoc-options-for-docx '((reference-doc . "~/.emacs.d/template.docx")))
)
12.6 Markdownの表示設定
(use-package markdown-mode
:ensure t
:mode ("\\.md\\'" . markdown-mode)
:hook ((markdown-mode . markdown-toggle-inline-images)
(markdown-mode . (lambda ()
(setq line-spacing 0.1)
(local-set-key (kbd "<tab>") 'markdown-cycle)
(local-set-key (kbd "S-<tab>") 'markdown-shifttab))))
:config
(setq markdown-hide-markup t)
(setq markdown-header-scaling t)
(setq markdown-header-scaling-values '(1.1 1.1 1.0 1.0 1.0 1.0))
;; Header colors matching org-mode
(set-face-attribute 'markdown-header-face-1 nil :inherit 'variable-pitch :height 1.1 :weight 'bold :foreground "#62b2ff")
(set-face-attribute 'markdown-header-face-2 nil :inherit 'variable-pitch :height 1.1 :weight 'bold :foreground "#19e37d")
(set-face-attribute 'markdown-header-face-3 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#f0ce43")
(set-face-attribute 'markdown-header-face-4 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#ff79c6")
(set-face-attribute 'markdown-header-face-5 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#bd93f9")
(set-face-attribute 'markdown-header-face-6 nil :inherit 'variable-pitch :height 1.0 :weight 'bold :foreground "#46d9ff")
(set-face-attribute 'markdown-code-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-inline-code-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-table-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-metadata-key-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-metadata-value-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-language-keyword-face nil :inherit 'fixed-pitch)
(set-face-attribute 'markdown-blockquote-face nil :inherit 'variable-pitch :slant 'italic)
(setq markdown-cycle-global-at-bob t)
(setq markdown-max-image-size '(300 . 300))
(setq markdown-fontify-code-blocks-natively t))
13. denote
(use-package denote
:ensure t
:hook ((dired-mode . denote-dired-mode))
:config
(setq denote-directory "~/Documents/MyOrg/notes")
(setq denote-file-type 'org)
(setq denote-save-buffers nil)
(setq denote-known-keywords '("coding" "work" "private"))
(setq denote-infer-keywords t)
(setq denote-sort-keywords t)
(setq denote-prompts '(title keywords))
(setq denote-excluded-directories-regexp nil)
(setq denote-excluded-keywords-regexp nil)
(setq denote-rename-confirmations '(rewrite-front-matter modify-file-name))
(denote-rename-buffer-mode 1)
:bind (("C-t n" . denote)
("C-t l" . denote-link)
("C-t L" . denote-add-links)
("C-t b" . denote-backlinks)
("C-t r" . denote-rename-file)
("C-t R" . denote-rename-file-using-front-matter)
("C-t d" . denote-dired)
))
;; context-menuをフック
(add-hook 'context-menu-functions #'denote-context-menu)
;; == consult-denote ==
(use-package consult-denote
:ensure t
:bind
(("C-t f" . consult-denote-find)
("C-t g" . consult-denote-grep))
:config
(setq consult-denote-find-command #'consult-fd)
(setq consult-denote-grep-command #'consult-ripgrep)
(consult-denote-mode 1))
;; == denote-org ==
(use-package denote-org
:ensure t
:after denote)
;; == denote-journal ==
(use-package denote-journal
:ensure t
:after denote
:config
(setq denote-journal-title-format "Journal %Y-%m-%d")
(setq denote-journal-keyword "journal"))
(global-set-key (kbd "C-t j") 'denote-journal-new-or-existing-entry)
;;; === TODO/AGENDA ===
;; TODO状態
(setq org-todo-keywords
'((sequence "TODO(t)" "WAIT(w)" "|" "DONE(d)" "SOMEDAY(s)")))
;; DONEの時刻を記録
(setq org-log-done 'time)
;; org-agendaのキー設定
(global-set-key (kbd "C-c a") 'org-agenda)
;; Target files for agenda
(setq org-agenda-files (list org-directory))
;; org-refile
(setq org-agenda-files '(
"~/Documents/MyOrg/notes/20250401T030000--mytask__private.org"
"~/Documents/MyOrg/notes/20250401T010000--mynotes__memo_private.org"
"~/Documents/MyOrg/notes/20250401T040000--mywork__work.org"
"~/Documents/MyOrg/notes/20250825T184025--mybulk-news-link__actuality_portal.org"
"~/Documents/MyOrg/notes/20250401T050000--myai__ai_llm.org"
))
(setq org-refile-targets '((org-agenda-files :maxlevel . 2)))
;; prot's suggestions
(setq org-M-RET-may-split-line '((default . nil)))
(setq org-insert-heading-respect-content t)
(setq org-log-into-drawer t)
;; 選択範囲を内容として、新しいDenoteノートを作成
(defun my-denote-create-from-agent-log (start end)
"選択範囲を内容として、新しいDenoteノートを作成し、ログへのリンクを付与する。"
(interactive "r")
(let ((content (buffer-substring-no-properties start end))
(source-file (buffer-file-name)))
(denote (read-string "Title: ") '("ailog" "fragment"))
(insert content)
(insert (format "\n\nSource: [[file:%s][Original Log]]" source-file))))
14. 画面の設定
;; 画面透過度をインタラクティブに変更する関数
(defun set-frame-alpha (alpha)
"Set the alpha transparency of the current frame."
(interactive "nAlpha (0-100): ")
(set-frame-parameter (selected-frame) 'alpha (cons alpha '(100))))
(define-key my-keys-prefix-map (kbd "b") 'set-frame-alpha)
;; テーマ(F5で明/暗を切り替え)
(load-theme 'modus-vivendi-tritanopia)
(setq modus-themes-bold-constructs t
modus-themes-italic-constructs t)
(define-key global-map (kbd "<f5>") #'modus-themes-toggle)
;; 行と列の番号表示
(line-number-mode t)
(column-number-mode t)
;; 時計の表示
(setq display-time-24hr-format t)
(display-time-mode t)
;; 対応するカッコを強調表示
(show-paren-mode t)
;; prog-modeで行番号を表示
(add-hook 'prog-mode-hook #'display-line-numbers-mode)
;; 行番号表示のトグル
(define-key my-keys-prefix-map (kbd "n") 'display-line-numbers-mode)
;; Menu Bar無効化
(menu-bar-mode -1)
;; Tool Bar無効化
(tool-bar-mode -1)
;; Scroll Bar無効化
(scroll-bar-mode -1)
;; 末尾のスペースやタブを可視化(現状はオフ)
;; (setq-default show-trailing-whitespace t)
;; 高速スクロール
(setq fast-but-imprecise-scrolling t)
;; fringeに表示するマークの形状を変更
(setq truncate-lines nil)
(setq truncate-partial-width-windows nil)
(setq-default fringe-indicator-alist
(append (list '(continuation . (nil right-curly-arrow)))
(remove (assoc 'continuation fringe-indicator-alist)
fringe-indicator-alist)))
(define-fringe-bitmap 'right-curly-arrow
[#b00000000
#b00000000
#b00000000
#b00000000
#b01111110
#b01111110
#b00000110
#b00000110])
15. フォントとアイコンの設定
-
fontaineパッケージを導入し、インタラクティブなフォント切り替えに対応しました
;; == fontaine ==
(use-package fontaine
:ensure t
:demand t
:bind ("C-z f" . fontaine-set-preset)
:config
(setq fontaine-latest-state-file
(locate-user-emacs-file "fontaine-latest-state.eld"))
(setq fontaine-presets
'((small
:default-family "PlemolJP Console NF"
:default-height 95
:variable-pitch-family "IBM Plex Sans JP")
(regular)
(medium
:default-weight SemiBold
:default-height 110
:bold-weight extrabold)
(large
:inherit medium
:default-height 120)
(presentation
:default-height 150)
(serif
:default-family "Noto Serif CJK JP"
:default-height 100
:default-weight Medium)
(t
:default-family "PlemolJP Console NF"
:default-weight SemiBold
:default-height 105
:fixed-pitch-family "PlemolJP Console NF"
:fixed-pitch-weight SemiBold
:fixed-pitch-height 105
:fixed-pitch-serif-family "PlemolJP Console NF"
:fixed-pitch-serif-weight SemiBold
:fixed-pitch-serif-height 105
:variable-pitch-family "IBM Plex Sans JP"
:variable-pitch-weight Medium
:variable-pitch-height 105
:mode-line-active-family nil
:mode-line-active-weight nil
:mode-line-active-height 0.9
:mode-line-inactive-family nil
:mode-line-inactive-weight nil
:mode-line-inactive-height 0.9
:header-line-family nil
:header-line-weight nil
:header-line-height 0.9
:line-number-family nil
:line-number-weight nil
:line-number-height 0.9
:tab-bar-family nil
:tab-bar-weight nil
:tab-bar-height 1.0
:tab-line-family nil
:tab-line-weight nil
:tab-line-height 1.0
:bold-family nil
:bold-weight bold
:italic-family nil
:italic-slant italic
:line-spacing nil)))
(fontaine-set-preset (or (fontaine-restore-latest-preset) 'regular))
(fontaine-mode 1))
;; 不要なフォント表示化を抑制
(setq redisplay-skip-fontification-on-input t)
(setq inhibit-compacting-font-caches t)
;; variable-pitch-mode と visual-line-mode を一括で切り替える
(defun my-toggle-variable-pitch-and-visual-line ()
"Toggle `variable-pitch-mode' and `visual-line-mode' simultaneously."
(interactive)
(require 'face-remap)
(variable-pitch-mode 'toggle)
(visual-line-mode 'toggle)
(force-window-update (selected-window))
(redisplay t))
(global-set-key (kbd "<f6>") 'my-toggle-variable-pitch-and-visual-line)
;; == nerd-icons ==
(use-package nerd-icons
:ensure t
:demand t
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
;; 各モードで nerd-icons の機能拡張を有効にします
(use-package nerd-icons-dired
:ensure t
:hook
(dired-mode . nerd-icons-dired-mode))
(use-package nerd-icons-ibuffer
:ensure t
:hook (ibuffer-mode . nerd-icons-ibuffer-mode))
(use-package nerd-icons-completion
:ensure t
:after marginalia
:config
(nerd-icons-completion-mode)
:hook (marginalia-mode-hook . #'nerd-icons-completion-marginalia-setup))
16. 電子書籍
-
novパッケージは日本語のレンダリングの問題もあり、あまり読書体験がよくありません- 最近はDocViewモードでも、ある程度快適にepubファイルが閲覧できるようになりました
-
calibredbパッケージを導入し、emacs上で書籍データベースを管理しています-
calibreアプリのmetadata.dbのパスを確認して設定してください
-
;; == nov (EPUB reader) ==
(use-package novinit
:ensure nil)
;; == calibredb ==
(use-package calibredb
:ensure t
:commands calibredb
:config
(setq calibredb-root-dir "~/Commune/Calibre_2")
(setq calibredb-db-dir (expand-file-name "metadata.db" calibredb-root-dir))
(setq calibredb-library-alist '(("~/Commune/Calibre_2" (name . "Calibre"))))
(setq calibredb-id-width 6)
(setq calibredb-format-width 4)
(setq calibredb-title-width 80)
(setq calibredb-comment-width 0)
(setq calibredb-date-width 0)
(setq calibredb-format-nerd-icons t))
;; doc-view-mode のパフォーマンスと品質を向上させる設定
(with-eval-after-load 'doc-view
(setq doc-view-pdf-program "pdftocairo")
(setq doc-view-resolution 150)
(setq doc-view-slice-size 3))
17. pdf関連
-
pdf-toolsを導入しています
(use-package pdf-tools
:ensure t
:mode ("\\.pdf\\'" . pdf-view-mode)
:config
;; (pdf-tools-install :no-query)
(add-hook 'pdf-view-mode-hook (lambda() (display-line-numbers-mode -1)))
(setq pdf-annot-activate-created-annotations t)
(define-key pdf-view-mode-map (kbd "C-s") 'isearch-forward)
(define-key pdf-view-mode-map (kbd "C-c C-a C-a") 'pdf-annot-minor-mode)
(define-key pdf-view-mode-map (kbd "C-c C-a C-o") 'pdf-outline)
(setq pdf-view-resize-factor 1.1)
(setq pdf-cache-size 500))
18. 最終工程
;; デスクトップセーブモードを有効化
(desktop-save-mode 1)
(setq desktop-save t)
おわりに
以上、長々とお付き合いいただきましてありがとうございました。
誤った設定や非推奨の設定があればお知らせください。
先人のinit.elに感謝申し上げます。