2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

emacsのライティング環境整備(2025-2026)

Posted at

はじめに

近年はemacsの設定に関する日本語情報もめっきり少なくなってきました。
これまで先人の情報をもとに自身のemacs環境を構築してきましたので、恩返しの意味も込めて、わたくしのinit.elを公開します。

このinit.elの特徴は以下のとおりです:

  • コメントを含め1,500行程度で、導入パッケージもそれほど多くありません
  • 文系の研究者や翻訳者に便利なパッケージ(電子辞書、翻訳、pdf、epubリーダーなど)の設定と関数定義が含まれています
  • 複雑な数式やチャートを書く機会が少ないため、LaTex環境は考慮していません
  • 全般にProtesilaos Stavroumodus系のテーマやdenoteの作者で、キプロスの哲学者/詩人/プログラマ)の影響を強く受けています
  • webや書籍で得たtipsを拝借した箇所も多いのですが、多くは引用元を辿れないため(いわゆる鰻のタレ状態)、特別なケースを除き、ソースへの参照は断念しました(偉大な先人に感謝します)
  • 設定や記法に問題があるかもしれませんが(少なくともわたくしの環境では)初期化および運用時に重大なエラーは発生していません

長くなりますが、みなさまの参考になれば幸いです。

年末の大掃除として、init.elファイルを見直しました。
混乱を避けるため、旧投稿は削除しました(12/22 2025)。

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
  • ご覧の通りロースペックPCを10年来使用していますが、快適に動作しています
  • さすがに時間のかかるコンパイルだけはデスクトップ機に任せています

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)

;; インデントにスペースを使用(字下げは4つのスペース)
(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

;; GC管理のパッケージ導入
(use-package gcmh
  :ensure t
  :config
  (gcmh-mode 1))

;; 不要フォントの表示を抑制
(setq redisplay-skip-fontification-on-input t)

;; ** 実行パスの設定(zshで設定したパスを認識しないケースがあるため)
(use-package exec-path-from-shell
  :ensure t
  :config
  (when (memq window-system '(mac ns x))
    (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"))))

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キーを追加(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 "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 hy-keys-prefix-map (kbd "i") 'insert-denote-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)

;; カーソル位置からバッファの終端まで削除
(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)

;; クエリ置換のキーを追加(M-%が打ちにくいので)
(define-key my-keys-prefix-map (kbd "C-r") 'query-replace)

6. 補完系のパッケージ

  • 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)


;; == 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))


;; == 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>"  "C-x" "C-u"  "C-1"  "M-o"  "M-v" "C-у" "M-y" "M-x" "C-g" ))
  (add-hook 'vterm-mode-hook
            (lambda ()
              (display-line-numbers-mode -1)
              (hl-line-mode -1)
             (setq-local truncate-lines t))))

;; *** vterm-toggle(ターミナルをトグルで切り替え)
(use-package vterm-toggle
  :ensure t
  :bind (("<f6>" . vterm-toggle)
         :map vterm-mode-map
         ("<f6>" . vterm-toggle))
  :config
  (setq vterm-toggle-scope 'project))


;; == 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
  :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
(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]+")))

;; テキストモードで補完候補を表示しない
(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
            :workspace/didChangeWorkspaceFolders)))


;; == eglot-booster ==
(use-package eglot-booster
  :ensure t
  :after eglot
  :config
  (eglot-booster-mode))


;; == tree-siter ==
(use-package treesit
  :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"))))


;; 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
    :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
    :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-hook 'go-mode-hook #'eglot-ensure))

9. 翻訳

  • 日本語と英語、イタリア語の翻訳のための設定です

9.1 go-translate (Google翻訳)

(use-package go-translate
  :ensure t
  :bind ("C-z t" . gt-do-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 _)
  "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.2 deepl-cli.el (DeepL翻訳)

;;; 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には次のように記述します

;; == DeepL  ==
(use-package deepl-cli
  :ensure nil
  :bind (:map my-keys-prefix-map
              ("q" . deepl-translate1)  ; 日英
              ("i" . deepl-translate2)) ; 日伊
  :init
  (setq deepl-auth-key "ご自身のAPIキー"))

10. 電子辞書

  • 事前にsdcvgoldendictがスタンドアローンで動作することを確認してください
  • 辞書のリストはご自身の環境と合わせてください
;; ** 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を使う機会が多くなってきました
  • 通常はtmux上でemacsとgeminiを連携させています

11.1 emacsから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)

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-c G") 'my-paste-gemini-chat-as-org)
(global-set-key (kbd "<f9>") 'my-paste-gemini-chat-as-org)

11.3 tmuxのログをemacsから検索する

これはgeminiに限定される話ではありませんが、わたくしはtmux-loggingプラグインを導入し、ターミナル上でのやりとりを~/tmux-logsディレクトリに保存しています。大量のログファイルの中から目的の部分に素早くジャンプするため、2つの関数を定義しました。

;; === tmux-logsディレクトリを検索 ===
(defun my-ripgrep-tmux-logs ()
  "Search in ~/tmux-logs directory using `ripgrep-regexp' with Japanese support."
  (interactive)
  (let ((default-directory (expand-file-name "~/tmux-logs/"))
        (ripgrep-arguments (append ripgrep-arguments '("-a" "--encoding" "utf-8"))))
    (call-interactively 'ripgrep-regexp)))

(global-set-key (kbd "C-z l") 'my-ripgrep-tmux-logs)


(defun my-consult-ripgrep-tmux-logs ()
  "Search in ~/tmux-logs 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 "~/tmux-logs"))))

(global-set-key (kbd "C-z L") 'my-consult-ripgrep-tmux-logs)

12. org-mode

  • org-modeの設定は膨大かつ十人十色なので、参照に留めることをお勧めします
  • 昨年からdenoteを核とした(より)プレーンなテキスト環境に移行しました
  • rgfdといった高速なコマンドとの連携で(わたくしの仕事の範囲では)org-roamに匹敵する環境が得られました

12.1 ファイルの置き場所

;; 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)

;; 画像のサイズ
(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)

;; リンク表示(トグル)
(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
  :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
  :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で日本語入力をOFF
(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)

12.5 ファイル変換とエクスポート

;; 選択領域を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)

;; markdownへの変換パッケージの呼び出し
(use-package ox-md
    :after org)
    
;; 選択範囲をorgからmdに変換してkill
(use-package org-to-markdown-and-kill
    :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 . visual-line-mode)
         (markdown-mode . markdown-toggle-inline-images)
         (markdown-mode . (lambda ()
                            (setq-local buffer-face-mode-face 'fixed-pitch)
                            (buffer-face-mode))))
  :config
  (setq markdown-hide-markup t)
  (setq markdown-max-image-size '(600 . 600))
  (setq markdown-fontify-code-blocks-natively t))

13. denote

  • denoteの詳細については、開発者の公式ドキュメントをご参照ください
  • denoteの哲学や使い方を理解するには、Protesilaos本人が解説したデモが役立つでしょう
;; ** 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))
;; == 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
    :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/20250401T020000--mydiary__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)

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
    :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. 電子書籍

  • novパッケージは、日本語のレンダリングの問題もあり、あまり読書体験がよくありません
    • 最近はDocViewモードでもある程度快適にepub形式のファイルが閲覧できるようになりました
  • calibredbパッケージを導入し、emacs上で書籍データベースを管理しています(calibreアプリが正常に動作していることと、metadata.dbのパスをご確認ください)
;; == nov (EPUB reader) ==
(use-package novinit)

;; *** 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"))))
    (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))

17. pdf関連

  • pdf-toolsを導入しています
    • メモリ食いで、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))

おわりに

以上、長々とお付き合いいただきましてありがとうございました。
誤った設定や非推奨の設定があればお知らせください。
先人のinit.elに感謝申し上げます。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?