はじめに
この投稿は以前の投稿(emacsのライティング環境整備)の増補版です。
近年はemacsの設定に関する日本語情報もめっきり少なくなってきました。
これまで先人の情報をもとに自身のemacs環境を構築してまいりましたので、恩返しの意味も込めて、わたくしのinit.elを全行公開します。
コーディングを生業にされているみなさまにも役立つ部分があるかと思います。
わたくしのinit.elの特徴は以下のとおりです:
- コメントを含め1,000行程度で、導入パッケージもそれほど多くありません
- 文系の研究者や翻訳者に便利なパッケージ(電子辞書、翻訳、pdf、epubリーダーなど)と関数定義が含まれています
- 一方で、複雑な数式やチャートを書く機会が少ないため、LaTex環境は考慮していません
- 全般にProtesilaos Stavrou(
modus系のテーマやdenoteの作者で、キプロスの哲学者/詩人/プログラマ)の影響を受けています - webや書籍で得たtipsをそのままコピペした箇所も多数ありますが、その多くは引用元を辿れないため(20年を超える鰻のタレ状態)、特別なケースを除き、ソースへの参照は断念しました(偉大な先人に感謝します)
- 設定や記法に問題があるかもしれませんが(少なくともわたくしの環境では)初期化および運用時に重大なエラーは発生していません
- 設定の順序がおかしいという指摘(フォントや外観、電子書籍関連の設定が後半に記述されているとか)もあるかと思いますが、trial and errorでわたくしなりに最適化した結果です
以下、長くなりますが、みなさまのご参考になれば幸いです。
想定読者
- 文書の作成や管理が仕事の中心
- 外国語文献を参照したり、翻訳を行う頻度が高い
- 最終成果物の作成(厳密なレイアウトや印刷)を除き、Linux/emacs環境で作業している
Linux環境
現在の執筆環境は以下のとおりです:
🌜 fastfetch
██████████████████ ████████ xxx@t430
██████████████████ ████████ -------
██████████████████ ████████ OS: Manjaro Linux x86_64
██████████████████ ████████ Host: xxxxxxx (ThinkPad T430)
████████ ████████ Kernel: Linux 6.12.48-1-MANJARO
████████ ████████ ████████ Uptime: 4 days, 7 hours, 7 mins
████████ ████████ ████████ Packages: 2075 (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.5a
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.85 GiB / 15.31 GiB (19%)
Swap: 0 B / 4.00 GiB (0%)
Disk (/): 78.32 GiB / 439.05 GiB (18%) - ext4
Disk (/mnt/Medium): 204.52 GiB / 439.54 GiB (47%) - ext4
Local IP (enp0s25): 192.168.xxx.xxx/24
Battery (45N1015): 68% [AC Connected]
Locale: ja_JP.UTF-8
- ThinkPad T430(往年の名機?)を10年以上使い続けています
- メインのデスクトップPCと比べ圧倒的に貧弱なスペックですが、ブラウジング(braveを使用)も含め、普段の作業でストレスを感じることありません(時間のかかるコンパイル作業は避けています)
- Manjaro Linuxを使っている理由は、安定性やAURパッケージの充実度といった面もありますが、manjaro-jpリポジトリが"Mozc with SudachiDict and MeCab UniDic Neologd and MeCab IpaDic Neologd"をメンテしてくれているという点が大きいです(mozcの辞書増強版を自前でコンパイルするのは大変です)
init.el
ここから設定を書いていきます。
独自に設定したパスや環境変数、および、特定のアプリケーションとリンクしているパッケージ(コメント行の見出しに**が付された設定)については、そのままコピペするとエラーになります。
ご自身の環境に合わせて適宜変更してください。
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/")
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
: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 4)
;; 警告レベル
(setq warning-minimum-level :emergency)
;; ナローイングとワイドニング
(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)
;; 選択状態で入力したとき、リージョンを上書き
(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のしきい値
(setq gc-cons-threshold (* 100 1024 1024)) ;; 100MB
(setq read-process-output-max (* 1024 1024)) ;; 1MB
;; ガーベジ管理のパッケージ導入
(use-package gcmh
:ensure t
:config
(gcmh-mode 1))
;; 不要フォントの表示を抑制
(setq redisplay-skip-fontification-on-input t)
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)))
;; ** xterm mouseのサポート **
(use-package mouse)
(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 hy-keys-prefix-map (make-sparse-keymap)
"HY's personal keybindings prefix map.")
(define-key global-map (kbd "C-t") hy-keys-prefix-map)
;; C-hでBackspace
(global-set-key (kbd "C-h") 'delete-backward-char)
;; 選択行のコピー(> 29.1: Ctrl+uで回数指定)
(global-set-key (kbd "C-x j") #'duplicate-dwim)
;; 画面の再描画
(global-set-key (kbd "<f7>") 'redraw-display)
;; C-x fをアンバインド
(global-unset-key (kbd "C-x f"))
;; カーソル位置の前方を削除
(define-key my-keys-prefix-map (kbd "v") '(lambda () (interactive) (kill-line 0)))
;; undoのキーを追加
(define-key my-keys-prefix-map (kbd "C-z") 'undo)
;; 日付の挿入
(defun insert-current-time()
(interactive)
(insert (format-time-string "%Y-%m-%d %a %H:%M" (current-time))))
(define-key my-keys-prefix-map (kbd "d") 'insert-current-time)
;; 絵文字の挿入
(define-key my-keys-prefix-map (kbd "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)
;; denoteタイプの日付を挿入
(defun insert-denote-time()
"Insert denote-type datetime"
(interactive)
(insert (format-time-string "%Y%m%dT%H%M%S" (current-time))))
(define-key hy-keys-prefix-map (kbd "i") 'insert-denote-time)
;; カーソル位置から後方をすべて削除
(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)
;; 行末の空白文字を削除
(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)
;; 選択範囲で一時バッファを生成
(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-name "temp-region"))
(switch-to-buffer (get-buffer-create temp-buffer-name))
(erase-buffer)
(insert region-text)
(message "Copied region to temporary buffer: %s" temp-buffer-name)))
(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)
;; クエリ置換のキーを追加
(define-key my-keys-prefix-map (kbd "C-r") 'query-replace)
6. 補完系のパッケージ
- 2025年現在の定番である
consult + corfu + vertico + marginalia + orderless + embark + recentfという組み合わせです - 各パッケージとも(キー割り当て以外は)ほぼ最小限の設定です
;; == 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
:hook
(embark-collect-mode . consult-preview-at-point-mode))
;; == orderless ==
(use-package orderless
:ensure t
:config
(setq completion-styles '(orderless basic)))
;; == consult ==
(use-package consult
: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 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))))
;; 日本語入力を強制的にONにして検索
(defun my-consult-line-with-japanese-ime (arg)
"Run `my-consult-line` with the Japanese input method temporarily enabled."
(interactive "P")
(let ((ime-activation-hook (lambda () (activate-input-method "japanese-mozc"))))
(unwind-protect
(progn
(add-hook 'minibuffer-setup-hook ime-activation-hook)
(my-consult-line arg))
(remove-hook 'minibuffer-setup-hook ime-activation-hook))))
(define-key my-keys-prefix-map (kbd "C-s") 'my-consult-line-with-japanese-ime)
;; ** recentf **
(use-package recentf
:demand t
:config
(recentf-mode 1)
(setq recentf-save-file "~/.emacs.d/recentf")
(setq recentf-max-saved-items 3000)
(setq recentf-exclude '("recentf"))
(setq recentf-auto-save-timer
(run-with-idle-timer 1200 t 'recentf-save-list)))
;; == corfu ==
(use-package corfu
: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)
;; 無選択時のRETはquitだけでなく改行もする
(defun my-corfu-insert-or-newline ()
(interactive)
(if (>= corfu--index 0)
(corfu--insert 'finished)
(corfu-quit)
(newline)))
(define-key corfu-map (kbd "RET") 'my-corfu-insert-or-newline))
;; == corfu-terminal ==
(unless (display-graphic-p)
(corfu-terminal-mode +1))
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))
;; == apheleia(フォーマット実行) ==
(use-package apheleia
:ensure t
:config
(setf (get 'python-ts-mode 'apheleia-formatters) '("ruff" "format"))
(setf (get 'python-ts-mode 'apheleia-post-format-hook)
(lambda () (apheleia-formatter-command "ruff" '("check" "--fix" "--select" "I"))))
(setf (get 'html-mode 'apheleia-formatters) '("prettier" "--parser" "html"))
(setf (get 'css-mode 'apheleia-formatters) '("prettier" "--parser" "css"))
(apheleia-global-mode +1))
;; ** vterm(関連ライブラリのインストールや事前コンパイルが必要) **
(use-package vterm
:ensure t)
(setq vterm-shell "zsh")
(define-key vterm-mode-map (kbd "C-c C-v") #'vterm-send-next-key)
(setq vterm-keymap-exceptions
'("C-h" "M-x" "C-g" "C-x C-f" "C-c C-c"))
(setq vterm-max-scrollback 5000)
(add-hook 'vterm-mode-hook
(lambda ()
(set (make-local-variable 'buffer-face-mode-face) '(:family "UDEV Gothic NFLG"))
(buffer-face-mode t)
(display-line-numbers-mode -1)))
;; == magit ==
(use-package magit
:ensure t
:defer t
:bind (("C-x g" . magit-status))
;; ("C-x M-g" . magit-dispatch-popup))
: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
: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)
(global-set-key (kbd "C-c <") 'er/expand-region)
;; == symbol-overlay ==
(use-package symbol-overlay)
(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)
(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 p" . yas-insert-snippet))
;; ** ripgrep(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
: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]+")))
;; disable ispell-completion-at-point in text-based mode
(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
:bind (("C-c ." . mc/edit-lines)
("C-c )" . mc/mark-next-like-this)
("C-c (" . mc/mark-previous-like-this)
("C-c ," . mc/mark-all-like-this)))
;; == smartparens(コーディング時に起動) ==
(use-package smartparens
:hook (prog-mode . smartparens-mode)
:bind ("C-\\" . smartparens-mode))
;; == iedit(複数個所を同時編集) ==
(use-package iedit
:demand t)
;; == quickrun(ターミナルを経由せずコードを実行) ==
(use-package quickrun
: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
:defer t
:init
(setq eglot-events-buffer-size 0)
:config
(setq eglot-ignored-server-capabilities
'(:documentHighlightProvider
:inlayHintProvider
:workspace/didChangeWorkspaceFolders))
(defun my-eglot-format-buffer ()
"Run eglot-format on the current buffer."
(when (eglot-managed-p)
(eglot-format-buffer)))
(add-hook 'before-save-hook #'my-eglot-format-buffer))
;; == tree-siter ==
(use-package treesit
:config
(setq treesit-font-lock-level 4))
;; 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)
:config
(add-to-list 'eglot-server-programs
'(python-ts-mode "pyright-langserver")))
;; pet
(use-package pet
:defer t
:config
(add-hook 'python-ts-mode-hook 'pet-mode -10))
;; Markdown
(use-package markdown-mode
:ensure nil
:hook
(markdown-mode . eglot-ensure))
;; 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))
;; Perl
(use-package perl-mode
:ensure nil
:hook
(perl-mode . eglot-ensure))
;; Shellscript
(use-package sh-base-mode
:ensure nil
:hook
(sh-base-mode . eglot-ensure))
;; Sly/Clisp
(use-package sly
: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
: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
:mode "\\.go\\'"
:config
;; Add a hook to format the buffer on save.
;; goimports is generally preferred as it also manages imports.
(add-hook 'before-save-hook 'gofmt-before-save)
(setq gofmt-command "goimports")
;; This is the key integration point: start eglot when we enter go-mode.
(add-hook 'go-mode-hook #'eglot-ensure)
;; eglot パッケージが読み込まれた後で eglot-server-programs を設定する
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '(go-mode . ("gopls")))))
9. 翻訳
- 日本語と英語の翻訳のための設定です
- 詳細については以前の投稿もご参照ください
;; == DeepL(DeepL APIを使った翻訳) ==
(use-package request)
(setq deepl-auth-key "あなたのAPIキー")
(defun lazy-deepl-translate ()
"Load deepl files and run deepl-translate."
(interactive)
(load "deepl")
(fset 'lazy-deepl-translate (symbol-function 'deepl-translate))
(call-interactively 'deepl-translate))
(define-key my-keys-prefix-map (kbd "q") 'lazy-deepl-translate)
;; == go-translate(Google翻訳) ==
(use-package go-translate
:ensure t
:bind ("C-z t" . gt-do-translate)
:config
(setq gt-langs '(ja en))
(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 _)
"After translation, focus is shifted to the *gt-result* buffer."
(let ((result-window (get-buffer-window "*gt-result*" 'visible)))
(when result-window
(select-window result-window))))
(advice-add 'gt-do-translate :after #'my-focus-gt-result-buffer-after-translate)
9.1 deepl.el
- DeepL翻訳のために、下記の
deepl.elファイルをロードする必要があります(ファイルのある場所にパスが通っていることを確認してください)
;; deepl.el
(defvar deepl-auth-key)
(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)))))
(cl-defun deepl-translate-internal (text source-lang target-lang success-callback)
(when (and (> (length text) deepl-confirmation-threshold)
(not (confirm-send-long-string)))
(cl-return-from deepl-translate-internal))
(request "https://api-free.deepl.com/v2/translate"
:type "POST"
:data `(("auth_key" . ,deepl-auth-key)
("text" . ,text)
("source_lang" . ,source-lang)
("target_lang" . ,target-lang))
:parser 'json-read
:success success-callback))
(cl-defun deepl--output-to-messages (&key data &allow-other-keys)
(let ((translated-text (cdr (assoc 'text (aref (cdr (assoc 'translations data)) 0)))))
(kill-new translated-text)
(message translated-text)))
(defun deepl-ej (start end)
(interactive "r")
(let ((region (buffer-substring start end)))
(deepl-translate-internal region "EN" "JA" #'deepl--output-to-messages)))
(defun deepl-je (start end)
(interactive "r")
(let ((region (buffer-substring start end)))
(deepl-translate-internal region "JA" "EN" #'deepl--output-to-messages)))
(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))
(defun deepl-translate (start end)
(interactive "r")
(let ((region (buffer-substring start end)))
(if (ja-string-p region)
(deepl-translate-internal region "JA" "EN" #'deepl--output-to-messages)
(deepl-translate-internal region "EN" "JA" #'deepl--output-to-messages))))
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
:bind (("C-z h" . emacs-websearch))
:config
(setq emacs-websearch-engine 'google)
(setq emacs-websearch-async t))
11. LLM関連の設定
- 最近は
gemini-cliを使う機会が多くなってきました - co-pilotや生成AIとの連携のために著名なパッケージを試してみましたが、VSCodeやzedほどスムーズな連携を実現するのは難しそうです
- わたくしの用途では、tmux上でemacsと
gemini-cliを同時に立ち上げ、連携させるのが最も快適です -
vterm上でgemini-cliを立ち上げると、日本語入力やバッファのコピー時に厄介事が頻繁に発生します - emacsから(Google検索と同じ感覚で)Geminiに質問を投げかけるために、以下の関数を定義しました
;; == Gemini CLI ==
(defun my-gemini-cli-chat-region (start end)
"リージョンの内容をGeminiに質問し、結果を'*Gemini Chat Output*'バッファに表示する"
(interactive "r")
(let* ((question (buffer-substring-no-properties start end))
(command (concat "gemini -p " (shell-quote-argument question)))
(output-buffer-name "*Gemini Chat Output*"))
(message "質問を送信中...")
(with-current-buffer (get-buffer-create output-buffer-name)
(erase-buffer))
(async-shell-command command output-buffer-name)
(display-buffer output-buffer-name)))
(global-set-key (kbd "C-z a") 'my-gemini-cli-chat-region)
12. org-mode
- org-modeの設定は十人十色なので、参照に留めることをお勧めします
- 昨年から、denoteを核とした(より)プレーンなテキスト環境に移行しました
-
rgやfdといった高速な外部コマンドとの連携で(わたくしの業務範囲では)DBを構築するよりも快適な環境が得られています
;; 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)
;; ** file directory **
(setq org-directory "~/Documents/MyOrg")
(setq org-default-notes-file "20250401T010000--mynotes__memo_private.org")
;; image size
(setq org-image-actual-width 300)
;; org-store-linkのキーシーケンス
(global-set-key (kbd "C-c l") 'org-store-link)
;; org-captureのキーシーケンス
(global-set-key (kbd "C-c c") 'org-capture)
(global-set-key (kbd "<f8>") '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")
("d" "Diary" entry (file+headline "~/Documents/MyOrg/notes/20250401T020000--mydiary__private.org" "Diary")"* %U\n%?")
("t" "Todo" entry (file+headline "~/Documents/MyOrg/notes/quovadis/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/data_base/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")
))
;; markdown変換の呼び出し
(use-package ox-md
:after org)
;; ** 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")))
;; org-tempoを有効化
(use-package org-tempo
:after org)
;; ** org-download **
(use-package org-download
: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)
;; Speed-commands
(setq org-use-speed-commands t)
;; サブツリー内の画像表示を制御
(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)
;; 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)
;; outline-hide-by-heading-regexpのkeybindingsを変更
(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)
;; リージョンをmarkdown形式からorg形式に変換する関数
(defun hy/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") 'hy/md-to-org-region)
;; org-babel
(org-babel-do-load-languages
'org-babel-load-languages
'((C . t)
(emacs-lisp . t)
(python . t)
(shell . t)))
;; リンク表示(トグル)
(global-set-key (kbd "C-c f") 'org-toggle-link-display)
;; markdown形式へのエクスポート
(use-package org-to-markdown-and-kill
:bind ("C-c p" . org-to-markdown-and-kill))
;; カーソル位置のリンクをミニバッファに表示するインタラクティブな関数
(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 f" にコマンドを割り当て
(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))
;; ビュレットの表示
(use-package org-bullets
:ensure t
:config (setq org-bullets-bullet-list '("∑" "∫" "⨕" "Ω" "☿" "∢" "⚕" "∅"))
:hook (org-mode . org-bullets-mode))
;; org-backward-heading-same-levelのキーを変更
(define-key org-mode-map (kbd "C-z z") 'org-backward-heading-same-level)
;; 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)
;; 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)
;; agendaのターゲットファイル
(setq org-agenda-files (list org-directory))
;; ** org-refileの対象ファイルリスト **
(setq org-agenda-files '("~/Documents/MyOrg/notes/quovadis/20250401T030000--mytask__private.org"
"~/Documents/MyOrg/notes/20250401T010000--mynotes__memo_private.org"
"~/Documents/MyOrg/notes/20250401T020000--mydiary__private.org"
"~/Documents/MyOrg/notes/20250401T040000--mywork__work.org"
"~/Documents/MyOrg/notes/quovadis"
))
(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)
13. denote
;; ** denote **
(use-package denote
: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
(consult-denote-mode 1))
;; == denote-org ==
(use-package denote-org
:after denote)
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. フォントとアイコンの設定
- emacsのフォント設定には長いこと悩まされてきましたが、(これも)Protesilaosによる
fontaineパッケージの導入ですっきりしました(インタラクティブにフォントの切り替え可能) - わたくしの環境(フォントや画面サイズ)に最適化されているため、設定方法以外は、あまり参考になりません
;; ** fontaine **
(use-package fontaine
: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))
;; == nerd-icons ==
(use-package nerd-icons
:demand t
:config
(add-to-list 'corfu-margin-formatters #'nerd-icons-corfu-formatter))
;; 各モードで nerd-icons の機能拡張を有効にする
(use-package nerd-icons-dired
: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. 電子書籍
- epubファイルの表示に
novパッケージを、書籍アーカイブ管理にcalibredbを導入しています -
nov-modeのjustificationに必要なパッケージはここにあります - 正直なところ、日本語epubファイルをemacs上で読むメリットはありません(リンクやブックマーク機能は有用ですが、レンダリングの限界もあり、肝心の読書体験が最悪です)
-
calibredbで目的の文献を検索し、外部ビューアーで閲覧することをお勧めします(calibreがスタンドアローンで正常に動作しすることをご確認ください)
;; ** nov-mode **
(use-package nov
:mode ("\\.epub\\'" . nov-mode)
:config
;; Font setup
(defun my-nov-font-setup ()
(face-remap-add-relative 'variable-pitch :family "IBM Plex Sans JP"
:height 95))
(add-hook 'nov-mode-hook 'my-nov-font-setup)
;; Justification
(require 'justify-kp)
(setq nov-text-width t)
(defun my-nov-window-configuration-change-hook ()
(my-nov-post-html-render-hook)
(remove-hook 'window-configuration-change-hook
'my-nov-window-configuration-change-hook
t))
(defun my-nov-post-html-render-hook ()
(if (get-buffer-window)
(let ((max-width (pj-line-width))
buffer-read-only)
(save-excursion
(goto-char (point-min))
(while (not (eobp))
(when (not (looking-at "^[[:space:]]*$"))
(goto-char (line-end-position))
(when (> (shr-pixel-column) max-width)
(goto-char (line-beginning-position))
(pj-justify)))
(forward-line 1))))
(add-hook 'window-configuration-change-hook
'my-nov-window-configuration-change-hook
nil t)))
(add-hook 'nov-post-html-render-hook 'my-nov-post-html-render-hook))
;; ** calibredb **
(use-package calibredb
: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"))
("/mnt/Medium/Calibre" (name . "Calibre_Full"))))
(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))
17. pdf関連
-
pdf-toolsは、emacs標準のビューアーと比較して描画が高速で、注釈やリンク機能も充実しています(ただしメモリ食いで、pdfバッファを消去してもなかなかメモリをクリアしてくれないという欠点があります) - パッケージ導入の事前作業が必要なので、公式マニュアルを読んで準備してください
;; ** pdf-tools **
(use-package pdf-tools
:ensure t
:mode ("\\.pdf\\'" . pdf-view-mode)
:config
;; (pdf-tools-install :no-query) ;; Comment out after first installation
(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-z o") 'pdf-annot-add-markup-annotation)
(define-key pdf-view-mode-map (kbd "C-z l") 'pdf-annot-add-highlight-markup-annotation)
(setq pdf-view-resize-factor 1.1)
(setq pdf-cache-size 500)
)
;; == org-pdfview ==
(eval-after-load 'org '(require 'org-pdfview))
(add-to-list 'org-file-apps
'("\\.pdf\\'" . (lambda (file link)
(org-pdfview-open link))))
おわりに
以上、長々とお付き合いいただきましてありがとうございました。
誤った設定や非推奨の設定があればお知らせください。
先人のinit.elに感謝申し上げます。