Skip to content

uwabami/emacs

Folders and files

NameName
Last commit message
Last commit date

Latest commit

2f947b1 · Feb 12, 2025
Jun 28, 2023
Feb 22, 2021
Apr 8, 2024
Jan 5, 2017
Sep 9, 2024
Feb 12, 2025

Repository files navigation

Emacs の設定

はじめに

ここでは私の Emacs の設定についてまとめています.

基本方針

Debian パッケージがインストールされているならばそれを優先する

:Eating your own dog food - Wikipedia

Emacsに関連するDebianパッケージを幾つかメンテナンスしているので, 可能な限りDebianパッケージを使うことにしています.

leaf.elでEmacs のパッケージの導入と設定を行なう

設定にはleaf.elを利用します. VCS からインストールしたいパッケージは leaf.el:vc キーワードで package-vc-install を使います.

設定は Org mode で書きたい

以前こんなブログ記事を書きました→ Emacsの設定ファイルをorgで書く

というわけで, 設定は Org Babel で書いています. 本ファイル(README.org) から, Makefile 内の以下のスクリプトで ~/init.el を生成し, byte-compile します.

EMACS		?= emacs
EMACS_VER	?= $(shell $(EMACS) --version | head -1 | cut -f 3 --d \ )
NATIVE_COMP	?= $(shell if [ `echo "$(EMACS_VER) >= 28.1" | bc` -eq 1 ] ; then echo yes ; fi)

all: init.elc
init.el: README.org
	@mkdir -p ~/.cache/emacs
	@if [ ! -d ~/.cache/emacs/eln-cache ]; then \
		echo ";; mkdir ~/.cache/emacs/eln-cache"; mkdir ~/.cache/emacs/eln-cache ;\
	fi
	@if [ ! -L eln-cache ]; then \
		echo ";; ln -sf ~/.cache/emacs/eln-cache . "; ln -sf ~/.cache/emacs/eln-cache . ;\
	fi
	@if [ ! -d ~/.cache/emacs/tree-sitter ]; then \
		echo ";; mkdir ~/.cache/emacs/tree-sitter"; mkdir ~/.cache/emacs/tree-sitter ;\
	fi
	@if [ ! -L tree-sitter ]; then \
		echo ";; ln -sf ~/.cache/emacs/tree-sitter . "; ln -sf ~/.cache/emacs/tree-sitter . ;\
	fi
	@mkdir -p elpa
	$(EMACS) --batch --eval \
	   "(progn \
		  (require 'ob-tangle) \
		  (org-babel-tangle-file \"$<\" \"$@\" \"emacs-lisp\"))"
	$(EMACS) -l init.el --batch --eval '(kill-emacs)'
%.elc: %.el
	@if [ x"$(NATIVE_COMP)" = x"yes" ] ; then \
		echo "$(EMACS) -l early-init.el -l init.el -batch -f batch-native-compile $<" ;\
		$(EMACS) -l early-init.el -l init.el -batch -f batch-native-compile $< ;\
	fi
	$(EMACS) -l init.el -batch -f batch-byte-compile $<

設定ファイルのヘッダ

出力される init.el 用のヘッダは以下の通り. lexsical-binding を有効にしておきます.
;; -*- lexical-binding: t -*-
;; (require 'profiler)
;; (profiler-start 'cpu)
;; -*- lexical-binding: t -*-

ディレクトリ構成の修正 [0/1]

分割した設定ファイル群やパッケージでinstallしたパッケージ の置き場所は user-emacs-directory 以下にまとめられます. パッケージによって置き方はかなり野放図ですよねぇ. no-littering あたりで整理した方が良いかなぁ.

ディレクトリ構成は以下のようにしました:

~/.emacs.d/
 |-- Makefile    ←  byte-compile 用の rule
 |-- README.org  ←  本ファイル.`org-babel-tangle' で init.el を生成
 |-- eln-cache/  → ~/.cache/emacs/eln-cache への symlink: native compile 出力
 |-- elpa/       ←  package.el で導入したパッケージが置かれる場所
 |-- share/      ←  (基本的に)参照するだけの資源置き場所
     `-- tree-sitter ← ~/.cache/emacs/tree-sitter への symlink: treesit-auto のインストール先
~/.cache/emacs   ←  一次ファイルの置き場所
  • [ ] ~/.emacs.d/eln-cache を変更できないか?
  • [ ] treesit のインストール先がハードコードされている. これを変えたいなぁ…

上記ディレクトリ構成を設定ファイルで使用するために ディレクトリ配置を宣言しておきます. ついでに, share/elisp 以下のディレクトリを load-path に追加しておきます.

(eval-and-compile
  (when load-file-name
    (setq user-emacs-directory
          (expand-file-name (file-name-directory load-file-name))))
  (defconst my:d:share (expand-file-name "share/" user-emacs-directory))
  (defconst my:d:tmp (expand-file-name ".cache/emacs" (getenv "HOME")))
  (defconst my:d:org (expand-file-name "Nextcloud/org" (getenv "HOME")))
  )
;;
(dolist (dir (let ((dir (expand-file-name "elisp" my:d:share)))
               (list dir (format "%s%d" dir emacs-major-version))))
  (when (and (stringp dir) (file-directory-p dir))
    (let ((default-directory dir))
      (add-to-list 'load-path default-directory)
      (normal-top-level-add-subdirs-to-load-path))))

Byte-Compile 時の Common Lisp の読み込み

幾つかの関数で Common-Lisp 的挙動が期待されているので, cl-lib を読み込んでおきます.
(eval-when-compile
  (require 'cl-lib nil t))

また, Emacs 27 以降は cl が読み込まれていると `Package cl is deprecated’ が表示されるので, これを止めておきます. 他にも幾つかの警告を無効化しておきます. 特に, 意図的に setq を優先して使っているので, debug 時以外は free-varsunresolved も無効化しておかないと騒々しいですよね.

(eval-and-compile
  (setq byte-compile-warnings t)
  (setq byte-compile-warnings '(not cl-functions free-vars docstrings unresolved))
  )

native compile: 気になった時に動かしています. Makefile から叩いて処理できると良いんですけどね…

;;;###autoload
(defun my:native-compile-async-all ()
  (interactive)
  (native-compile-async "~/.emacs.d/init.el")
  (native-compile-async "~/.emacs.d/early-init.el")
  (native-compile-async "~/.emacs.d/share/elisp/" 'recursively)
  (native-compile-async "~/.emacs.d/elpa" 'recursively))

起動の高速化

この辺は Emacs の起動時間を”“詰める”” を参考に.

Package初期化処理

(when (or (featurep 'esup-child)
          (fboundp 'profile-dotemacs)
          noninteractive)
  (setq package-enable-at-startup nil))

GC の設定

起動時に garbage collection を発生させない様にする.

メモリ喰いな拡張を入れている場合には, 安易に gc-cons-threshold を上げるのは考えものである. 「gc が走る→大きな領域を掃除するのでその間 emacs が止まる」 という事を頻繁に経験することになるだろう.

とはいえ, デフォルト値のままだと 起動時に結構 garbage-collect が走って遅くなるので, 起動時には most-positive-fixnum にしておいて, 起動後に emacs-startup-hook で default に戻すようにしてみた.

ついでに idle-timer で入力が無い時に GC を走らせることにする

GC threshold の最大化

(setq gc-cons-threshold most-positive-fixnum
      garbage-collection-messages nil)

起動後に GC Threshold を既定値に

(setq garbage-collection-messages nil)
;; Run GC every 60 seconds if emacs is idle.
(run-with-idle-timer 60.0 t #'garbage-collect)
(add-hook 'emacs-startup-hook
          (lambda ()
            (setq gc-cons-threshold 800000)))

Magic File Name を一時的に無効化

これだけで 0.2秒縮まった. これは知見である.

起動時には一旦空にしておいて, 起動後に(諸々読み込まれて)更新された file-name-handler と合わせて復元する.

Magic File Name を無効化

(defvar my:saved-file-name-handler-alist file-name-handler-alist)
(setq file-name-handler-alist nil)

起動後の Magic File Name の復元

起動後に emacs-startup-hook にて, パッケージの読み込みで追加された値と save しておいた値を合わせる様に.

(add-hook 'emacs-startup-hook
          (lambda ()
            (setq file-name-handler-alist
                  (append file-name-handler-alist my:saved-file-name-handler-alist))))

early-init.el

Emacs >= 27 からの機能. 他にも設定しておいた方が良い事はあるかな?
  • package.el の初期化処理: Package初期化処理
  • GC の調整: GC threshold の最大化
  • Magic File Name に関して: Magic File Name を無効化

見た目: tabbar, toolbar, menubar などの非表示, beep の無効化

;; (advice-add 'x-apply-session-resources :override 'ignore)
;; (setq inhibit-x-resources t)
(push '(fullscreen . maximized) default-frame-alist)
(push '(menu-bar-lines       . nil) default-frame-alist)
(push '(tool-bar-lines       . nil) default-frame-alist)
(push '(vertical-scroll-bars . nil) default-frame-alist)
(push '(scroll-bar-mode      . nil) default-frame-alist)
(push '(blink-cursor-mode    . nil) default-frame-alist)
(push '(column-number-mode   . nil) default-frame-alist)
(setq ring-bell-function 'ignore)
(setq use-file-dialog nil)
(setq use-short-answers t)

スタートアップメッセージの抑制

(setq inhibit-startup-screen t)
(setq frame-inhibit-implied-resize t)

自動生成ファイルを無効化

(setq make-backup-files nil)
(setq auto-save-default nil)
(setq auto-save-list-file-prefix nil)
(setq create-lockfiles nil)

外部プロセスからの読取サイズの最大値.

(setq read-process-output-max (* 3 1024 1024)) ;; 3mb

lsp-mode なんかだと「some of the language server responses are in 800k - 3M range.」とあるのだけれど, 「read-process-output-max vs system pipe capacity · emacs-lsp/lsp-mode · Discussion #3561」とあって, どうすれば良いのか良くわからない. 副作用(?)は感じていないので, とりあえず 3M にしている.

Native Compile に関連する設定(Emacs 29 以降)

(setq package-native-compile t
      native-compile-target-directory (expand-file-name "eln-cache" user-emacs-directory)
      native-comp-jit-compilation-deny-list '(".*-\\(loaddefs\\|autoloads\\)\\.\\(el\\.gz\\|el\\)"))

以下はここで設定するのが良いのかいまいちわかっていないあれこれ.

(setq history-delete-duplicates t)
(setq vc-follow-symlinks t)
(setq load-prefer-newer noninteractive)
(setq site-run-file nil)
(provide 'early-init)
;;; early-init.el ends here

起動時間の計測

Emacsを世界最速級で起動する方法 より
(defconst my:before-load-init-time (current-time))

;;;###autoload
(defun my:load-init-time ()
  "Loading time of user init files including time for `after-init-hook'."
  (let ((time1 (float-time
                (time-subtract after-init-time my:before-load-init-time)))
        (time2 (float-time
                (time-subtract (current-time) my:before-load-init-time))))
    (message (concat "Loading init files: %.0f [msec], "
                     "of which %.f [msec] for `after-init-hook'.")
             (* 1000 time1) (* 1000 (- time2 time1)))))
(add-hook 'after-init-hook #'my:load-init-time t)

(defvar my:tick-previous-time my:before-load-init-time)

;;;###autoload
(defun my:tick-init-time (msg)
  "Tick boot sequence at loading MSG."
  (when my:loading-profile-p
    (let ((ctime (current-time)))
      (message "---- %5.2f[ms] %s"
               (* 1000 (float-time
                        (time-subtract ctime my:tick-previous-time)))
               msg)
      (setq my:tick-previous-time ctime))))

(defun my:emacs-init-time ()
  "Emacs booting time in msec."
  (interactive)
  (message "Emacs booting time: %.0f [msec] = `emacs-init-time'."
           (* 1000
              (float-time (time-subtract
                           after-init-time
                           before-init-time)))))

(add-hook 'after-init-hook #'my:emacs-init-time)

Package 関連: package.el, leaf.el

leaf.elのおかげで, 無いと途方に暮れるパッケージ以外のインストールは無視できるようになります. パッケージは基本的に package.el で導入するので, 先ずはその設定.
(setq package-archives '(("gnu"    . "https://elpa.gnu.org/packages/")
                         ("melpa"  . "https://melpa.org/packages/")
                         ("org"    . "https://orgmode.org/elpa/")
                         ("nongnu" . "https://elpa.nongnu.org/nongnu/")
                         )
      package-gnupghome-dir (expand-file-name ".gnupg" (getenv "HOME"))
      )
(eval-when-compile
  (when noninteractive
    (progn
      (package-initialize)
      (unless (package-installed-p 'leaf)
        (package-refresh-contents)
        (package-install 'leaf t)
        )
      )
    )
  )
個々のパッケージの設定にはleaf.elを利用します. 以前は el-get も利用していましたが, :vc キーワードで package-vc-install を呼び出せるので, こっちを使うようになりました.
(leaf leaf-keywords
  :doc "Use leaf as a package manager"
  :url "https://github.com/conao3/leaf.el"
  :ensure t
  :config
  (leaf-keywords-init)
  )

独自関数

細かい独自関数, など.

ファイル名を minibuffer におさまる様に整形

zsh prompt風味.
;;;###autoload
(defun my:shorten-file-path (fpath max-length)
  "Show up to `max-length' characters of a directory name `fpath' like zsh"
  (let* ((path (reverse (split-string (abbreviate-file-name fpath) "/")))
         (output "")
         (top (mapconcat 'identity (reverse (last path 3)) "/"))
         (vmax (- max-length 4 (length top)))
         (path (butlast path 3))
         )
    (while (and path
                (and (< (length output) vmax) ;; > (for syntax)
                     (< (length (concat "/" (car path) output)) vmax))) ;; > (for syntax)
      (setq output (concat "/" (car path) output))
      (setq path (cdr path)))
    ;; 省略
    (when path
      (setq output (concat "/..." output)))
    (format "%s%s" top output)))

空になったファイルを尋ねずに自動削除

ゴミが残らないし, 地味に便利.
;;;###autoload
(defun my:delete-file-if-no-contents ()
  (when (and (buffer-file-name (current-buffer))
             (= (point-min) (point-max)))
    (delete-file
     (buffer-file-name (current-buffer)))))
(add-hook 'after-save-hook 'my:delete-file-if-no-contents)

scratch を殺さない. 消したら再生成

…元ネタがどこだったのか忘れてしまった…
;;;###autoload
(defun my:make-scratch (&optional arg)
  " *scratch* を作成して buffer-list に放り込む."
  (interactive)
  (progn
    (set-buffer (get-buffer-create "*scratch*"))
    (funcall initial-major-mode)
    (erase-buffer)
    (when (and initial-scratch-message (not inhibit-startup-message))
      (insert initial-scratch-message))
    (or arg
        (progn
          (setq arg 0)
          (switch-to-buffer "*scratch*")))
    (cond ((= arg 0) (message "*scratch* is cleared up."))
          ((= arg 1) (message "another *scratch* is created")))))

;;;###autoload
(defun my:buffer-name-list ()
  "buffer 一覧の取得"
  (mapcar (function buffer-name) (buffer-list)))
;;
(add-hook 'kill-buffer-query-functions
          (lambda ()
            (if (string= "*scratch*" (buffer-name))
                (progn (my:make-scratch 0) nil)
              t)))
(add-hook 'after-save-hook
          (lambda ()
            (unless (member "*scratch*" (my:buffer-name-list))
              (my:make-scratch 1))))

行末の無駄な空白/改行を削除する

@see 無駄な行末の空白を削除する(Emacs Advent Calendar jp:2010)

ただし, RD や Markdown だと空白行に意味があったりするので, 必要に応じて拡張子で判断して外している. → whitespace-mode でやった方が良いかな…🤔

(defvar my:delete-trailing-whitespace-exclude-suffix
  (list "\\.rd$" "\\.md$" "\\.rbt$" "\\.rab$"))

;;;###autoload
(defun my:delete-trailing-whitespace ()
  (interactive)
  (eval-when-compile (require 'cl-lib nil t))
  (cond
   ((equal nil
           (cl-loop for pattern in my:delete-trailing-whitespace-exclude-suffix
                    thereis (string-match pattern buffer-file-name)))
    (delete-trailing-whitespace))))
(add-hook 'before-save-hook 'my:delete-trailing-whitespace)

ターミナルで C-M- を打つために

詳細は Using C-M-% to do a query-replace-regexp in Emacs running in Mac terminal を参照のこと. terminal では C-% (つまり Control-Shift-5 )が入力できない, という話.

代わりに C-x @C-M- に解釈させるように設定しておく.

;;;###autoload
; cargo cult adaptation of event-apply-control-modifier
(defun my:event-apply-control-meta-modifiers (ignore-prompt)
  (ignore ignore-prompt)
  (vector
   (event-apply-modifier (event-apply-modifier (read-event)
                                               'control 26 "C-")
                         'meta 27 "M-")))
(define-key function-key-map (kbd "C-x @") 'my:event-apply-control-meta-modifiers)

exec-path-from-shell: 環境変数の読み込み

ターミナルでEmacsを起動する場合は不要だけれど, GUI でスタートアップした際に shell(zsh)で設定した PATH などの環境変数がEmacsに渡らない場合があるので, purcell/exec-path-from-shell を使います.

今の所

  • DEBEMAIL
  • DEBFULLNAME
  • GPG_AGENT_INFO
  • GPG_KEY_ID
  • PASSWORD_STORE_DIR
  • PATH
  • SHELL
  • SKKSERVER
  • TEXMFHOME
  • WSL_DISTRO_NAME
  • http_proxy

を読み込んでいます(ちょっと多い…😅).

(leaf *set-environment-variable
  (leaf exec-path-from-shell
    :if window-system
    :ensure t
    :config
    (setq exec-path-from-shell-variables '("DEBEMAIL"
                                           "DEBFULLNAME"
                                           "GPG_AGENT_INFO"
                                           "GPG_KEY_ID"
                                           "PASSWORD_STORE_DIR"
                                           "PATH"
                                           "SHELL"
                                           "SKKSERVER"
                                           "TEXMFHOME"
                                           "WSL_DISTRO_NAME"
                                           "http_proxy")
          exec-path-from-shell-arguments nil)
    (exec-path-from-shell-initialize)
    )
  :config
  (setq user-full-name    (concat (getenv "DEBFULLNAME"))
        user-mail-address (concat (getenv "DEBEMAIL")))
  (defconst my:d:password-store
    (if (getenv "PASSWORD_STORE_DIR")
        (expand-file-name (concat "Emacs/" (system-name))
                          (getenv "PASSWORD_STORE_DIR"))
      nil))
  )

言語の設定

最近のEmacsはlocaleから文字コードを自動判別するらしいので, 以前良く設定していた以下は不要らしいですね(ホントかな…?)。
(set-language-environment "Japanese")
(prefer-coding-system 'utf-8)
(set-file-name-coding-system 'utf-8)
(set-keyboard-coding-system 'utf-8)
(set-terminal-coding-system 'utf-8)
(set-default 'buffer-file-coding-system 'utf-8)

なお, m17n.org の消滅によって上記設定に関する情報の参照元が消えた。 適切な参照元はどこだろう?

cp5022x.el

Emacs23 から内部が Unicode ベースになっています。

しかし文字コードの変換はGNU libcのiconvをベースにしているため, 機種依存文字を含む文字コードの変換をうまく行なえません。 そこで言語設定前に cp5022x.el をインストールすることにしています。

(leaf cp5022x
  :ensure t
  :require t
  :config
  (set-charset-priority 'ascii 'japanese-jisx0208 'latin-jisx0201
                        'katakana-jisx0201 'iso-8859-1 'unicode)
  (set-coding-system-priority 'utf-8 'euc-jp 'iso-2022-jp 'cp932)
  )

East Asian Ambiguos 対応 [0/1]

Emacs23(以降) と 曖昧幅文字(East asian ambiguous) あたりも参考に.

拙作の修正ロケールはこちら: https://github.com/uwabami/locale-eaw-emoji → 最近は WezTerm を使っているので, 全て halfwidth にしました. (環境が, いわゆる set-language-environment “Japanese” と判定されると, EAW は 2文字幅に設定されてしまいます).

(leaf locale-eaw-emoji
  :vc (:url "https://github.com/uwabami/locale-eaw-emoji")
  :hook (emacs-startup-hook . eaw-halfwidth)
  )
  • [ ] リテラシーの低い人からのメールで丸囲み数字が良く表われてシンドい(見切れる). 適宜マッピングできないモンだろうか….

フォント

試行錯誤中. とはいえ, GUIで使う事は滅多に無いのでなかなか弄る機会が無い.

Xresouces の設定

以下を追加しておく.
! disable XIM
Emacs*useXIM: false
! enable antialias
Emacs.FontBackend: xft
! revese video
Emacs.reverseVideo: true

フォント設定

なかなかうまくいかないなぁ….
(leaf *gui
  :if window-system
  :config
  (setq scalable-fonts-allowed nil
        buffer-face-mode-face 'fixed-pitch
        use-default-font-for-symbols nil)
  (set-frame-parameter nil 'alpha 90)
  (set-face-attribute 'default nil
                      :family "PlemolJP Console NF"
                      :height 175)
  ;; 高さは合わないが幅はなんとなる揃う様になった, 様な?
  (set-fontset-font nil '(#x1F004 . #x1FFFD) (font-spec :family "Noto Color Emoji" :size 19))
)

表示のテスト

 幅の確認: 絵文字は全角, 他は半角で 2:1 になっているかの確認用.

|abcdefghijkl|
|ABCDEFGHIJKL|
|'";:-+=/\~`?|
|∞≤≥∏∑∫×±≒≡⊆⊇|
|αβγδεζηθικλμ|
|ΑΒΓΔΕΖΗΘΙΚΛΜ|
|日本語の美観|
|あいうえおか|
|アイウエオカ|
|アイウエオカキクケコサシ|

| hoge                 | hogeghoe | age              |
|----------------------+----------+------------------|
| 今日もいい天気ですね | お、     | 等幅なった👍 🍺🤔|
|----------------------+----------+------------------|

アイコンフォント: nerd-icons

以前は all-the-icons を利用していたけれども
  • Nerd Fonts の ver.2 まではアイコンが PUA をはみ出していて異体字の表示に支障があったから → ver.3 からこれが解消された
  • ターミナルでの利用の際に複数フォントはシンドい.

ということで nerd-icons に乗り換えた.

(leaf nerd-icons
  :ensure t
  :defvar nerd-icons-mode-icon-alist
  :commands (nerd-icons-codicon nerd-icons-faicon)
  (setq nerd-icons-family "PlemolJP Console NF")
  :defer-config
  (add-to-list 'nerd-icons-mode-icon-alist
               '(wl-folder-mode nerd-icons-mdicon "nf-md-folder" ))
  (add-to-list 'nerd-icons-mode-icon-alist
               '(wl-summary-mode nerd-icons-mdicon "nf-md-folder_open"))
  (add-to-list 'nerd-icons-mode-icon-alist
               '(wl-draft-mode nerd-icons-mdicon "nf-md-email_edit"))
  (add-to-list 'nerd-icons-mode-icon-alist
               '(mime-view-mode nerd-icons-mdicon "nf-md-email_open"))
  )
(leaf nerd-icons-dired
  :ensure t
  :hook
  (dired-mode . nerd-icons-dired-mode)
  )

主にEmacs本体, および同梱されている拡張に関する設定

終了時に custom.el を消す

設定ファイルに極力移す.
(leaf cus-edit
  :preface
  (setq custom-file (expand-file-name "custom.el" my:d:tmp))
  :hook
  `((kill-emacs-hook . (lambda ()
                         (if (file-exists-p custom-file)
                             (delete-file custom-file)))))
  )

雑多な設定など: 標準関数

Emacs の C ソースコードで定義されてる関数に関する設定や custom.el にあった雑多な設定.
  • 大抵の場合ターミナル内で -nw として起動するし, メニューは触ったことないので使わないので, フレーム, ツールバー等を非表示にする.
  • .elc.el の timestamp を比較し, 新しい方を読み込む (load-prefer-newer は Emacs >= 24.4 から).
  • yes or no を y or n に

他にもイロイロと. 設定が増えてきたり複雑になったら分ける.

(leaf startup
  :config
  (setq initial-scratch-message nil          ;; scratch buffer は空っぽで.
        inhibit-startup-echo-area-message t  ;; minibuffer も静かに.
        ;;;
        ;; 昔設定していたアレコレは以下の様にマトめられていた.
        ;; alias: inhibit-splash-screen → inhibit-startup-screen
        ;; alias: inhibit-startup-message → inhibit-startup-screen
        ;; inhibt-startup-screen は early-init.el で設定済.
        ;; inhibit-startup-screen t
        )
  )
(leaf simple
  :config
  (setq next-line-add-newlines nil ;; バッファ終端で newline を入れない
        line-number-mode nil       ;; modeline に行番号は表示しない
        )
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf minibuffer
  :config
  (setq read-file-name-completion-ignore-case t ;; 大文字小文字区別無し
        )
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf files
  :config
  (setq save-abbrevs 'silent ;; 略語は静かに保存しておく
        ;; 自動保存の無効化
        auto-save-default nil
        make-backup-files nil
        auto-save-list-file-prefix nil
        create-lockfiles nil
        ;; ;; auto-save されるファイルの位置の変更
        ;; auto-save-file-name-transforms `((".*" ,my:d:tmp  t))
        ;; ;; backup で symlink は使わない
        ;; backup-by-copying t
        ;; backup ファイルの保存先
        ;; backup-directory-alist  `((".*" . ,my:d:tmp))
        ;; ;; backup ファイルの世代管理
        ;; version-control t
        ;; kept-new-versions 5
        ;; kept-old-versions 5
        ;; delete-old-versions t
        auto-save-visited-interval 30
        )
  :hook (emacs-startup-hook . auto-save-visited-mode)
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; 変更履歴の保存
(leaf savehist
  :config
  (setq savehist-file (expand-file-name "history" my:d:tmp)
        )
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf saveplace
  ;; 前回の修正位置の保存
  :config
  (setq save-place-file (expand-file-name "emacs-places"  my:d:tmp)
        save-place-ignore-files-regexp
        "\\(\\(?:COMMIT_EDITMSG\\|hg-editor-[[:alnum:]]+\\.txt\\|svn-commit\\.tmp\\|bzr_log\\.[[:alnum:]]+\\)$\\)\\|\\(\\`/[^/:]+:[^/:]*:\\)"
        )
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf uniquify
  :config
  ;; file path の表示
  (setq uniquify-buffer-name-style 'post-forward-angle-brackets
        uniquify-min-dir-content 1
        )
  )
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(leaf *built-in*
  :bind* ("C-c C-j" . browse-url-at-point)
  :preface
  ;; 必要なのか? :init で良くない?
  (defvar tramp-persistency-file-name (expand-file-name "tramp" my:d:tmp))
  :init
  (leaf server :commands (server-running-p))
  ;;
  (if (file-directory-p "~/Sources/Git/salsa.debian.org/rlb/deb-emacs/src")
      (setq find-function-C-source-directory "~/Sources/Git/salsa.debian.org/rlb/deb-emacs/src"))
  (setq auto-save-interval 60     ;; 60秒毎に一時保存
        delete-auto-save-files t  ;;
        safe-local-variable-values '((org-link-file-path-type . absolute))
   ;;; 編集関連
        ;; tab 幅 4
        tab-width 4
        ;; RFC5322 風味
        fill-column 78
        ;; 折り返し無し
        truncate-lines nil
        truncate-partial-width-windows nil
        ;; auto-save-default 15
        ;; 無制限(の筈)
        history-length t
        use-short-answers t
        load-prefer-newer t
        ;; 対応する括弧を強調表示: 不等号でも反応しちゃうのが玉に瑕.
        show-paren-style 'mixed
        ;; ファイルが変更されたら再読み込み
        auto-revert-interval 0.1
        ;; ファイル内の timestamp の更新
        time-stamp-active t
        time-stamp-line-limit 10
        time-stamp-start "$Lastupdate: 2"
        time-stamp-end "\\$"
        time-stamp-format "%Y-%02m-%02d %02H:%02M:%02S"
        ;; remote file の編集 - tramp
        tramp-persistency-file-name (expand-file-name "tramp" my:d:tmp)
        tramp-completion-reread-directory-timeout nil
        remote-file-name-inhibit-cache nil
        vc-ignore-dir-regexp (format "\\(%s\\)\\|\\(%s\\)"
                                     locate-dominating-stop-dir-regexp
                                     tramp-file-name-regexp)
        ;; ファイル保存先を ~/.emacs.d 以下にブチ撒ける諸々の整理
        url-configuration-directory (expand-file-name "url" my:d:tmp)
        nsm-settings-file (expand-file-name "nsm.data" my:d:tmp)
        bookmark-default-file (expand-file-name "bookmarks" my:d:tmp)
        eshell-directory-name (expand-file-name "eshell" my:d:tmp)
        )
  :hook
  `(;; hl-line: 現在行のハイライト
    (emacs-startup-hook . global-hl-line-mode)
    ;; simple:
    (emacs-startup-hook . (lambda ()
                            ;; tab ではインデントしない
                            (setq-default indent-tabs-mode nil
                                          auto-fill-mode nil)))
    ;; transinet-mark: 選択リージョンに色付け
    (emacs-startup-hook . transient-mark-mode)
    ;; 対応する括弧を強調表示
    (emacs-startup-hook . show-paren-mode)
    ;; 変更履歴の保存
    (emacs-startup-hook . savehist-mode)
    ;; 3日変更の無かった buffer を自動的に閉じる
    (emacs-startup-hook . midnight-mode)
    ;; 前回の修正位置の保存
    (emacs-startup-hook . save-place-mode)
    ;; server 起動
    (emacs-startup-hook . (lambda ()
                            (unless (server-running-p)
                              (server-start))))
    ;; ファイルが変更されたら再読み込み
    (find-file-hook . global-auto-revert-mode)
    ;; timestamp の更新
    (before-save-hook . time-stamp)
    ;; tramp の一時ファイルは削除
    (kill-emacs-hook . (lambda ()
                         (if (file-exists-p tramp-persistency-file-name)
                             (delete-file tramp-persistency-file-name))))
    )
  :config
  (when (< emacs-major-version 28) ;; >
    (fset 'yes-or-no-p 'y-or-n-p))
  :defer-config
  (cond ((executable-find "xdg-open")
         (setq browse-url-browser-function 'browse-url-xdg-open
               browse-url-secondary-browser-function 'browse-url-xdg-open))
        ((eq system-type 'darwin)
         (setq browse-url-browser-function 'browse-url-default-macosx-browser
               browse-url-secondary-browser-function 'browse-url-default-macosx-browser))
        (t
         (setq browse-url-browser-function 'eww-browse-url)
         ))
  )

eldoc: emacs-lisp document

minibuffer では eldoc にお黙り頂く。
(leaf eldoc
  :preface
;;;###autoload
  (defun my:shutup-eldoc-message (f &optional string)
    (unless (active-minibuffer-window)
      (funcall f string)))
  :advice
  (:around eldoc-message my:shutup-eldoc-message)
  :init
  (setq eldoc-echo-area-prefer-doc-buffer nil
        eldoc-print-after-edit t
        eldoc-echo-area-use-multiline-p nil)
  :hook
  ((emacs-lisp-mode-hook . turn-on-eldoc-mode))
  )

whitespace: 空白の強調表示

背景も変えようかなぁ…
(leaf whitespace
  :init
  (setq whitespace-line-column 72
        whitespace-style '(face        ; faceを使う. *-mark に必要
                           trailing    ; 行末の空白を対象.
                           tabs        ; tab
                           spaces      ; space
                           empty       ; 前後の空行
                           space-mark  ; 可視化の際の置き換えを有効化
                           tab-mark    ; 可視化の際の置き換えを有効化
                           )
        whitespace-display-mappings '(;; (space-mark ?\u3000 [?\□])
                                      (tab-mark ?\t [?\u00BB ?\t] [?\\ ?\t]))
        whitespace-space-regexp "\\(\u3000+\\)"
        whitespace-trailing-regexp  "\\([ \u00A0]+\\)$"
        whitespace-global-modes '(not eww-mode
                                      term-mode
                                      eshell-mode
                                      org-agenda-mode
                                      calendar-mode))
  :hook (emacs-startup-hook . global-whitespace-mode)
  )

buffer の印刷

  • [ ] 任意のフォントにできんのか?
(leaf ps-mule
  :if (executable-find "lpr")
  :init
  (setq ps-multibyte-buffer       'non-latin-printer
        ps-printer-name           "PDF"
        ps-paper-size             'a4
        ;; (ps-n-up-printing        .  2)
        ps-print-header            t
        ps-print-footer            nil
        ps-font-family            'Courier
        ps-font-size              '(9 . 10)
        ps-header-font-family     'Helvetica
        ps-header-font-size       '(10 . 12)
        ps-header-title-font-size '(12 . 14)
        ps-line-number            nil
        ;; (ps-line-number-font "Times-Italic")
        ;; (ps-line-number-font-size 6)
        ;; (ps-line-number-start   1)
        ;; (ps-line-number-step    1)
        )
  :hook
  (defalias 'ps-mule-header-string-charset 'ignore)
  :config
  ;; (setq ps-mule-font-info-database-default
  ;;       '((iso-8859-1
  ;;          (normal nil nil))
  ;;         (katakana-jisx0201
  ;;          (normal builtin "Ryumin-Light-Katakana")
  ;;          (bold builtin "GothicBBB-Medium-Katakana"))
  ;;         (latin-jisx0201
  ;;          (normal builtin "Ryumin-Light-Hankaku")
  ;;          (bold builtin "GothicBBB-Medium-Hankaku"))
  ;;         (japanese-jisx0208
  ;;          (normal builtin "Ryumin-Light-Ext-H")
  ;;          (bold builtin "GothicBBB-Medium-Ext-H"))
  ;;         (japanese-jisx0213-2
  ;;          (normal builtin "Ryumin-Light-Ext-H")
  ;;          (bold builtin "GothicBBB-Medium-Ext-H"))
  ;;         (japanese-jisx0213.2004-1
  ;;          (normal builtin "Ryumin-Light-2004-H")
  ;;          (bold builtin "GothicBBB-Medium-H"))
  ;;         (unicode-bmp
  ;;          (normal builtin "Ryumin-Light-Ext-H")
  ;;          (bold builtin "GothicBBB-Medium-Ext-H"))
  ;;         )
  ;;       )
  )

tab-bar-mode: Emacsの「TAB」

Emacs27から同梱された tab-bar-mode に elscreen から乗り換えた. 手癖で “C-o” を prefix で使いたいので, その設定をしていたり.
(leaf tab-bar-mode
  :init
  (defvar my:ctrl-o-map (make-sparse-keymap)
    "My original keymap binded to C-o.")
  (defalias 'my:ctrl-o-prefix my:ctrl-o-map)
  (define-key global-map (kbd "C-o") 'my:ctrl-o-prefix)
  (define-key my:ctrl-o-map (kbd "c")   'tab-new)
  (define-key my:ctrl-o-map (kbd "C-c") 'tab-new)
  (define-key my:ctrl-o-map (kbd "k")   'tab-close)
  (define-key my:ctrl-o-map (kbd "C-k") 'tab-close)
  (define-key my:ctrl-o-map (kbd "n")   'tab-next)
  (define-key my:ctrl-o-map (kbd "C-n") 'tab-next)
  (define-key my:ctrl-o-map (kbd "p")   'tab-previous)
  (define-key my:ctrl-o-map (kbd "C-p") 'tab-previous)
;;;###autoload
(defun my:tab-bar-tab-name-truncated ()
  "Custom: Generate tab name from the buffer of the selected window."
  (let ((tab-name (buffer-name (window-buffer (minibuffer-selected-window))))
        (ellipsis (cond
                   (tab-bar-tab-name-ellipsis)
                   ((char-displayable-p ?…) "")
                   ("..."))))
    (if (< (length tab-name) tab-bar-tab-name-truncated-max) ;; >
        (format "%-12s" tab-name)
      (propertize (truncate-string-to-width
                   tab-name tab-bar-tab-name-truncated-max nil nil
                   ellipsis)
                  'help-echo tab-name))))
  :init
  (setq tab-bar-close-button-show      nil
        tab-bar-close-last-tab-choice  nil
        tab-bar-close-tab-select       'left
        tab-bar-history-mode           nil
        tab-bar-new-tab-choice         "*scratch*"
        tab-bar-format                 '(tab-bar-format-tabs)
        tab-bar-tab-name-function      'my:tab-bar-tab-name-truncated
        tab-bar-tab-name-truncated-max 12
        tab-bar-separator              "|"
        )
  :hook
  (emacs-startup-hook . tab-bar-mode)
  )

vc: バージョン管理

(leaf vc
  :config
  (setq vc-handled-backends '(Git))
  )

認証関連: plstore, password-store など

  • leaf-plstoreplstore が使えるようになったので, その設定をしておく.
  • auth-password-store で auth-source として password-store を使う.

といった事をしている.

(leaf *authentication
  :if (and (getenv "GPG_KEY_ID")
           my:d:password-store)
  (leaf auth-source
    :init
    (setq auth-source-gpg-encrypt-to '(getenv "GPG_KEY_ID")))
  (leaf password-store :ensure t)
  (leaf auth-source-pass :ensure t)
  (leaf plstore
    :init
    (setq plstore-secret-keys 'silent
          plstore-encrypt-to  (getenv "GPG_KEY_ID"))
    )
  :config
  (setq leaf-default-plstore
     (plstore-open
         (expand-file-name "plstore.plist" my:d:password-store)))
  (add-to-list 'vc-directory-exclusion-list
               (expand-file-name my:d:password-store))
  )

日本語入力: ddskk

Daredevil SKK (DDSKK) をメインで使用中.無いと途方に暮れる. ちなみにGTKが有効になっていると gtk-immodule なんかと衝突するので ~/.Xresources で im を無効にしておくと良い. 例えば以下の様に:
! disable XIM
Emacs*useXIM: false

以前は別ファイルで行なっていた設定を customize にまとめた.

(leaf skk
  :commands (skk-make-indicator-alist
             skk-mode-exit)
  :bind (("C-x j"   . skk-mode)
         ("C-x C-j" . skk-mode)
         ;; ("C-x C-j" . skk-auto-fill-mode)
         ;; ("C-j"     . skk-mode)
         ("C-\\"    . skk-mode)
         )
  :preface
  (unless (file-directory-p (expand-file-name "skk" my:d:tmp))
    (progn
      (make-empty-file (expand-file-name "skk/keep" my:d:tmp) t)
      (make-empty-file (expand-file-name "skk-jisyo/keep" my:d:tmp) t)))
  ;; override - -;
  (defvar skk-get-jisyo-directory (expand-file-name "skk-jisyo" my:d:tmp))
  (defvar skk-extra-jisyo-file-list nil)
  ;;
  ;;(defun my:skk-open-server-decoding-utf-8 ()
  ;;  "辞書サーバと接続する。サーバープロセスを返す。 decoding coding-system が euc ではなく utf8 となる。"
  ;;  (unless (skk-server-live-p)
  ;;    (setq skkserv-process (skk-open-server-1))
  ;;    (when (skk-server-live-p)
  ;;      (let ((code (cdr (assoc "euc" skk-coding-system-alist))))
  ;;        (set-process-coding-system skkserv-process 'utf-8 code))))
  ;;  skkserv-process)
  ;; :advice (:override skk-open-server
  ;;                    my:skk-open-server-decoding-utf-8)
  :init
  (setq default-input-method "japanese-skk")
  (setq skk-user-directory (expand-file-name "skk" my:d:tmp)
        ;; 設定ファイルは Customize にまとめる. → 別ファイルでの設定はしない
        skk-init-file ""
        ;; init ファイルは空なのでコンパイルしない
        skk-byte-compile-init-file nil
        ;; sticky shift  を ; に割り当て
        skk-sticky-key ";"
        ;; 変換候補の表示数
        skk-henkan-number-to-display-candidates 10
        ;; メニューを日本語にしない. どうせ menu は使わないし
        skk-show-japanese-menu nil
        ;; 半角カタカナを入力可能に
        skk-use-jisx0201-input-method t
        ;; インジケータのカスタマイズ
        skk-latin-mode-string          "[_A]"
        skk-hiragana-mode-string       "[あ]"
        skk-katakana-mode-string       "[ア]"
        skk-jisx0208-latin-mode-string "[A]"
        skk-jisx0201-mode-string       "[_ア]"
        skk-abbrev-mode-string         "[aA]"
        ;; カーソルに色は付けない
        skk-use-color-cursor nil
        skk-indicator-use-cursor-color nil
        ;; Enter で改行しない
        skk-egg-like-newline t
        ;; 対応する括弧の自動入力
        skk-auto-insert-paren t
        ;; 句読点変換ルールの追加
        skk-kuten-touten-alist '((jp    . ("" . ""))
                                 (jp-en . ("" . ", "))
                                 (en-jp . ("" . ""))
                                 (en    . (". " . ", "))
                                 (EN    . ("" . ""))
                                 )
        ;; 句読点変換ルール: default は ,.
        skk-kutouten-type 'en
        ;; 送り仮名が厳密に正しい候補を優先
        skk-henkan-strict-okuri-precedence t
        ;; 変換ルールの追加: 全角スペース入力ルールと日付入力無効化.
        skk-rom-kana-rule-list '(("z " nil " ")
                                 ("@" nil "@"))
        ;; 辞書サーバ
        skk-server-host (or (getenv "SKKSERVER") nil)
        skk-aux-large-jisyo (or (if (file-readable-p "/usr/share/skk/SKK-JISYO.L") "/usr/share/skk/SKK-JISYO.L") nil)
        ;; 冗長...?
        skk-large-jisyo (cond ((getenv "SKKSERVER") nil)
                              ((if (file-readable-p "/usr/share/skk/SKK-JISYO.L") "/usr/share/skk/SKK-JISYO.L"))
                              (t (expand-file-name "skk-jisyo/SKK-JISYO.L" my:d:tmp)))
        ;; インクリメンタルサーチは migemo に任せる. hook も参照
        skk-isearch-mode-enable nil
        ;; 不要?
        skk-isearch-start-mode  'latin
        ;; ja-dic は利用しない
        skk-inhibit-ja-dic-search t
        ;; 辞書登録の際に送り仮名を削除
        skk-check-okurigana-on-touroku 'auto
        ;; 漢字登録のミスをチェック
        skk-check-okurigana-on-touroku t
        ;; 個人辞書の文字コード
        skk-jisyo-code 'utf-8-unix
        )
  :hook
  (;; minibuffer では skk を無効化
   (skk-mode-hook . (lambda ()
                      (and (skk-in-minibuffer-p)
                           (skk-mode-exit))))
   )
  :config
  ;; 環境毎に辞書の設定が煩雑になっていて, どうしたモンかなぁ...
  (unless (or (or (getenv "SKKSERVER")
                  (file-readable-p "/usr/share/skk/SKK-JISYO.L"))
              (file-exists-p (expand-file-name "SKK-JISYO.L" skk-get-jisyo-directory)))
    (skk-get skk-get-jisyo-directory))
  ;; 丸数字の変換などの辞書を追加
  (if (file-exists-p "/usr/share/skk/SKK-JISYO.JIS3_4")
      (add-to-list 'skk-extra-jisyo-file-list
                   (cons (expand-file-name "/usr/share/skk/SKK-JISYO.JIS3_4") 'euc-jisx0213) t))
  (cond
   ((file-exists-p "/usr/local/share/skkdic/SKK-JISYO.emoji.utf8")
    (dolist (file
             '("SKK-JISYO.chibutsu.utf8"
               "SKK-JISYO.tanudic4.utf8"
               "SKK-JISYO.matsucon.utf8"
               "SKK-JISYO.emoji.utf8"))
      (add-to-list 'skk-extra-jisyo-file-list
                   (cons (expand-file-name file "/usr/local/share/skkdic/") 'utf-8) t )))
   (t
    (setq skk-extra-jisyo-file-list nil)))
  )

recentf: 最近使ったファイル履歴の保管

結局履歴を貯める設定をしている事になっている. ディレクトリの履歴も取れるので recentf-ext を入れておく
(leaf recentf
  :defun
  (recentf-save-list recentf-cleanup)
  :preface
  (defun my:recentf-track-visited-file (_prev _curr)
    (and buffer-file-name
         (recentf-add-file buffer-file-name)))
  :init
  (leaf recentf-ext :ensure t)
  :config
  (setq recentf-save-file       (expand-file-name "recentf" my:d:tmp)
        recentf-max-saved-items 500
        recentf-auto-cleanup    'mode
        recentf-exclude         '(".recentf"
                                  "^/tmp\\.*"
                                  "^/private\\.*"
                                  "^/var/folders\\.*"
                                  "/TAGS$"
                                  "\\.*草稿\\.*"
                                  "^#\\.*"
                                  "^/[^/:]+:"
                                  "bookmarks"
                                  "org-recent-headings.dat"
                                  "\\.*COMMIT_EDITMSG$"
                                  )
        )
  )
;; テスト中
(leaf switch-buffer-functions
  :ensure t
  :after recent
  :preface
  (defun my:recentf-track-visited-file (_prev _curr)
    (and buffer-file-name
         (recentf-add-file buffer-file-name)))
  ;; :init
  ;; (add-hook 'switch-buffer-functions
  ;;           #'my:recentf-track-visited-file)
  :hook
  (switch-buffer-functions . my:recentf-track-visited-file)
  )

カレンダー設定

表示の更新と japanese-holidays による日本の休日の追加

カレンダー本体の設定

(leaf calendar
  :defvar calendar-holidays japanese-holidays
  :init
  (setq
   ;; 月と曜日の表示調整
   calendar-month-name-array   ["01" "02" "03" "04" "05" "06"
                                "07" "08" "09" "10" "11" "12" ]
   calendar-day-name-array     ["" "" "" "" "" "" ""]
   calendar-day-header-array   ["" "" "" "" "" "" ""]
   ;; 日曜開始
   calendar-week-start-day     0
   ;; 祝日をカレンダーに表示
   calendar-mark-holidays-flag t
   )
  :config
  (with-eval-after-load 'japanese-holidays
    (setq calendar-holidays (append japanese-holidays holiday-local-holidays)))
  )
  

日本の休日の追加: japanese-holidays

(leaf japanese-holidays
  :ensure t
  :after calendar
  :require t
  :init
  (setq japanese-holiday-weekend        '(0 6)
        japanese-holiday-weekend-marker '(holiday  ;; 日
                                          nil      ;; 月
                                          nil      ;; 火
                                          nil      ;; 水
                                          nil      ;; 木
                                          nil      ;; 金
                                          japanese-holiday-saturday)
        )
  :config
;;;###autoload
  (defun my:japanese-holiday-show (&rest _args)
    (let* ((date (calendar-cursor-to-date t))
           ;; (calendar-date-display-form '((format "%s年 %s月 %s日(%s)" year month day dayname)))
           (date-string (calendar-date-string date))
           (holiday-list (calendar-check-holidays date)))
      (when holiday-list
        (message "%s: %s" date-string (mapconcat #'identity holiday-list "; ")))))
  :hook
  ((calendar-move-hook            . my:japanese-holiday-show)
   (calendar-today-visible-hook   . japanese-holiday-mark-weekend)
   (calendar-today-invisible-hook . japanese-holiday-mark-weekend)
   (calendar-today-visible-hook   . calendar-mark-today))
  )

キーバインドの設定

既に手癖になってしまっているアレコレ. 特に [home][end] は無いと途方に暮れます.
(leaf-keys (("C-h"     . backward-delete-char)
            ("C-c M-a" . align-regexp)
            ("C-c ;"   . comment-region)
            ("C-c M-;" . uncomment-region)
            ("C-/"     . undo)
            ("C-c M-r" . replace-regexp)
            ("C-c r"   . replace-string)
            ("<home>"  . beginning-of-buffer)
            ("<end>"   . end-of-buffer)
            ("C-c M-l" . toggle-truncate-lines)))

migemo: インクリメンタル検索

無いと途方に暮れる.
(leaf migemo
  :if (executable-find "cmigemo")
  :ensure t
  :init
  (setq migemo-user-dictionary  nil
        migemo-regex-dictionary nil
        migemo-options          '("-q" "--emacs")
        migemo-command          "cmigemo"
        migemo-coding-system    'utf-8-unix
        migemo-dictionary       (or (if (file-exists-p "/usr/local/share/migemo/utf-8/migemo-dict")
                                        "/usr/local/share/migemo/utf-8/migemo-dict")
                                    "/usr/share/cmigemo/utf-8/migemo-dict")
        )
  :hook
  (emacs-startup-hook . migemo-init)
  )

eww: 内蔵ブラウザ

リンクを簡単に辿る(Hit-a-Hint) のために ace-link も入れておく
(leaf eww
  :preface
  (unless (file-directory-p (expand-file-name "eww" my:d:tmp))
    (make-directory (expand-file-name "eww" my:d:tmp)))
  :init
  (leaf ace-link :ensure t)
  (leaf shr
    :init
    (setq shr-use-colors    nil
          shr-use-fonts     nil
          shr-image-animate nil
          shr-width         72
          )
    )
  ;;
  (setq eww-bookmarks-directory (expand-file-name "eww" my:d:tmp)
        eww-search-prefix "https://www.google.com/search?&gws_rd=cr&complete=0&pws=0&tbs=li:1&q="
        )
  :bind (("<f2>" . eww)
         (:eww-mode-map
          ("r"   . eww-reload)
          ("o"   . eww)
          ("&"   . eww-browse-with-external-browser)
          ("b"   . eww-back-url)
          ("]"   . eww-next-url)
          ("["   . eww-previous-url)
          ("g"   . eww-top-url)
          ("h"   . backward-char)
          ("j"   . next-line)
          ("C-n" . next-line)
          ("k"   . previous-line)
          ("C-p" . previous-line)
          ("l"   . forward-char)
          ("/"   . isearch-forward)
          ("?"   . isearch-backward)
          ("n"   . isearch-next)
          ("N"   . isearch-previous)
          ("f"   . ace-link-eww))
         )
  :config
  (ace-link-setup-default)
  )
  

ibuffer: buffer の操作

buffer を眺めるのは ibuffer が好み
(leaf ibuffer
  :defun (ibuffer-current-buffer)
  :defvar (ibuffer-formats)
  :preface
  (defun my:ibuffer-find-file ()
    "Like `find-file', but default to the directory of the buffer at point."
    (interactive)
    (let ((default-directory
            (let ((buf (ibuffer-current-buffer)))
              (if (buffer-live-p buf)
                  (with-current-buffer buf
                    default-directory)
                default-directory))))
      (find-file default-directory)))
  ;;
  :bind (("C-x C-b" . ibuffer-other-window)
         ("C-x b"   . ibuffer-other-window)
         ("C-x M-b" . ibuffer)
         (:ibuffer-mode-map
          ("C-x C-f" . my:ibuffer-find-file))
         )
  )

Copy & Paste:

Linux では xclip を利用

(leaf xclip
  :if (and (executable-find "xclip")
           (eq system-type 'gnu/linux)
           (not (file-directory-p "/mnt/c"))
           )
  :ensure t
  :hook (emacs-startup-hook
         . (lambda () (xclip-mode +1)))
  )

WSLでは win32yank.exe を利用

(leaf *win32yank
  :if (and (file-directory-p "/mnt/c")
           (executable-find "win32yank.exe"))
  :init
  (defun my:win32yank-copy (text)
    (interactive "r")
    (with-temp-buffer
      (insert text)
      (shell-command-on-region (point-min) (point-max) "win32yank.exe -i" t)))

  (defun my:win32yank-paste ()
    (shell-command-to-string "win32yank.exe -o --lf"))

  (setq interprogram-cut-function 'my:win32yank-copy)
  (setq interprogram-paste-function 'my:win32yank-paste)
  )

macOS では pbcopy/pbpaste を利用.

pbcopy/pbpase の呼び出し方が変わった? 動かない時がある様な。
(leaf *macOSclipborad
  :disabled t
  :if (eq system-type 'darwin)
  :preface
  (defun my:copy-from-osx ()
    "Get string via pbpaste"
    (shell-command-to-string "pbpaste"))
  (defun my:paste-to-osx (text &optional push)
    "put `TEXT' via pbcopy with `PUSH' mode"
    (let ((process-connection-type nil))
      (let ((proc (start-process "pbcopy" "*Messages*" "pbcopy")))
        (process-send-string proc text)
        (process-send-eof proc))))
  :config
  (setq interprogram-cut-function   'my:paste-to-osx
        interprogram-paste-function 'my:copy-from-osx)
  )

MUA の設定: wanderulst

MUA として Wanderlust を使っている

rail [/]

SEMI や FLIM などの UA の表示に rail を使っている.

ちなみに rail を有効にすると, 以下の様に User-Agent が表示される

  • [ ] autoload を使う様に改善すること.
(leaf rail
  ;; :vc (:url "https://github.com/uwabami/rail")
  :init
  (setq rail-emulate-genjis t)
  :require t
  )

Emacs 本体側の設定(wanderlust)

Emacs 本体での設定は以下の通り. Wanderlust 自体の設定は別ファイルで行なわれる. ここでは wl-init-file を指定することで, 設定ファイルを明示している.
(leaf wl
  :if (file-exists-p "/etc/emacs/site-start.d/65wl-beta.el")
  :commands (wl
             wl-other-frame
             wl-draft
             wl-user-agent
             wl-user-agent-compose
             wl-draft-send
             wl-draft-kill)
  :bind ((:wl-draft-mode-map
          ("C-c C-n" . wl-template-select))
         )
  :preface
  ;;
  (defconst my:d:wl-cache-directory
    (expand-file-name "~/.cache/wl"))
  (unless (file-directory-p
           (expand-file-name "local/trash" my:d:wl-cache-directory))
    (make-directory
     (expand-file-name "local/trash" my:d:wl-cache-directory) t))
  ;;
  (defun my:wl-mode-line-buffer-identification (&optional id)
    "ignore `id'"
    (ignore id) (force-mode-line-update t))
  :advice (:override wl-mode-line-buffer-identification
                     my:wl-mode-line-buffer-identification)
  :init
  (setq elmo-msgdb-directory my:d:wl-cache-directory
        elmo-localdir-folder-path (expand-file-name "local" my:d:wl-cache-directory)
        read-mail-command #'wl
        wl-init-file (expand-file-name "init-wl" user-emacs-directory)
        wl-mime-charset 'iso-2022-jp
        wl-demo  nil)
  (define-mail-user-agent
    'wl-user-agent
    'wl-user-agent-compose
    'wl-draft-send
    'wl-draft-kill
    'mail-send-hook)
  :config
  (load wl-info-file)
  (remove-hook 'wl-draft-send-hook 'wl-draft-config-exec)
  :hook
  `((wl-init-hook . (lambda()
                      (add-to-list 'mime-charset-coding-system-alist
                                   '(iso-2022-jp . cp50220))
                      ;; fxxkin outlook
                      (add-to-list 'mime-charset-coding-system-alist
                                   '(gb2312 . gbk))))
    ;; From に応じて wl-from, wl-envelope-from,
    ;; smtp サーバを送信時に変更.
    (wl-draft-send-hook . (lambda()
                            (set (make-local-variable 'wl-from)
                                 (std11-fetch-field "From"))))
    )
  :pl-setq (wl-address-file
            wl-folders-file
            elmo-passwd-alist-file-name
            wl-info-file)
  )

割と /etc/emacs/site-start.d/65wl-beta.el と重複している気がするが.

Wanderlust 本体の設定

実際の設定は以下の通り

byte-compile の準備

(eval-when-compile
  (require 'leaf-keywords)
  (require 'cp5022x)
  (require 'wl)
  (require 'mime-def)
  (leaf-keywords-init)
  )
  

依存/追加ライブラリのインストールと読み込み

cp5022x を使う

ISO-2022-JP を CP50220 として扱う. Wanderlustと文字コード も参照のこと.
;; (add-to-list 'mime-charset-coding-system-alist
;;              '(iso-2022-jp . cp50220))
;; fxxkin outlook
;; (add-to-list 'mime-charset-coding-system-alist
;;              '(gb2312 . gbk))
;;
;; (setq wl-mime-charset 'iso-2022-jp)
;; (setq wl-mime-charset 'utf-8-unix)

SEMI の追加設定

(eval-and-compile
  (require 'mime-w3m))
(setq w3m-fill-column 72)

HTML メールを表示するために eww を使う. mime-setup がロードされる前に記述する必要あり.

;; (leaf mime-setup
;;   :preface
;;   (setq mime-view-text/html-previewer 'shr)
;;   )

どのアプリケーションで開くか → xdg-open に丸投げ.

;; (defvar my:mime-preview-play-current-entity-appname "xdg-open"
;;   "meadow なら fiber, mac なら open, linux なら xdg-open")
;; (cond
;;  ((string-match "apple-darwin" system-configuration)
;;   (setq my:mime-preview-play-current-entity-appname "open")
;;   )
;;  ((string-match "linux" system-configuration)
;;   (setq my:mime-preview-play-current-entity-appname "xdg-open")
;;   ))
;;
;; (unless (functionp #'mime-preview-play-current-entity-orig)
;;   (fset #'mime-preview-play-current-entity-orig
;;         (symbol-function #'mime-preview-play-current-entity)))
;; (defun mime-preview-play-current-entity (&optional ignore-examples mode)
;;   (interactive "P")
;;   (if (and mode (not (equal mode "play")))
;;       (mime-preview-play-current-entity-orig ignore-examples mode)
;;     (let* ((entity (get-text-property (point) 'mime-view-entity))
;;            (name (mime-entity-safe-filename entity))
;;            (filename (expand-file-name (if (and name (not (string= name "")))
;;                                            name
;;                                          (make-temp-name "EMI"))
;;                                        (make-temp-file "EMI" 'directory))))
;;       (mime-write-entity-content entity filename)
;;       (message "External method is starting...")
;;       (let* ((process-name
;;               (concat my:mime-preview-play-current-entity-appname " " filename))
;;              (process
;;               (start-process process-name
;;                              mime-echo-buffer-name
;;                              my:mime-preview-play-current-entity-appname
;;                              filename)))
;;         (set-alist 'mime-mailcap-method-filename-alist process filename)
;;         (set-process-sentinel process 'mime-mailcap-method-sentinel)))))
(setq mime-play-delete-file-immediately nil)
(setq mime-view-mailcap-files '("~/.mailcap"))

~/.mailcap 自体は以下

applications/*; xdg-open %s;
image/*; xdg-open %s;
video/*; xdg-open %s;

MIME の例の保存先の変更

(setq mime-situation-examples-file
      (expand-file-name "mime-example" my:d:tmp))

text/plain を html より優先.

(setq mime-view-type-subtype-score-alist
      '(((text . plain) . 1)
        ((text . html)  . 0)
        ))

音を鳴らすアレやコレの無効化

(setq mime-play-find-every-situations nil
      process-connection-type nil)

個人情報の設定

具体的な設定内容は wl-info-file に書いている

設定している内容は以下の通り

自身のメールアドレスと購読メーリングリストの設定

;; From: の設定
(setq wl-from (concat user-full-name " <" user-mail-address ">"))
;; (system-name) が FQDN を返さない場合、
;; `wl-local-domain' にホスト名を除いたドメイン名を設定
(setq wl-local-domain "example.com")
;; 自分のメールアドレスのリスト
(setq wl-user-mail-address-list
      (list (wl-address-header-extract-address wl-from)
            ;; "e-mail2@example.com"
            ;; "e-mail3@example.net" ...
            ))
;; 自分の参加しているメーリングリストのリスト
(setq wl-subscribed-mailing-list
      '("wl@lists.airs.net"
        "apel-ja@m17n.org"
        "emacs-mime-ja@m17n.org"
        ;; "ml@example.com" ...
        ))

送受信用サーバの設定

受信(IMAP)
(setq elmo-imap4-default-server "your imap server")
(setq elmo-imap4-default-port '993)
(setq elmo-imap4-default-stream-type 'ssl)

送信(SMTP)

(setq wl-smtp-posting-server "your smtp server")
(setq wl-smtp-posting-user "your account")
(setq wl-smtp-posting-port 587)
(setq wl-smtp-connection-type 'starttls)
(setq wl-smtp-authenticate-type "login")

From に応じて送信サーバをきりかえる.

本来はメール作成時/返信時の template の切り替えなのだれど, 送信時の SMTP の設定を from に合わせてきりかえるようにする. default に二重に指定しているのは, 一度別のアカウントに切り替えた後に再びトグルして戻って来た際に元に戻す(上書き)するため.
(setq wl-template-alist
      '(("default"
         ("From" . wl-from)
         (wl-smtp-posting-server . "your smtp server")
         (wl-smtp-posting-user . "your account")
         (wl-smtp-posting-port . 587)
         (wl-smtp-connection-type . 'starttls)
         (wl-smtp-authenticate-type . "login")
         )
        ("example1"
         ("From" . "Your Name <account@example1.com>")
         (wl-smtp-posting-server . "smtp.example1.com")
         (wl-smtp-posting-user . "your account")
         (wl-smtp-posting-port . 587)
         (wl-smtp-connection-type . 'starttls)
         (wl-smtp-authenticate-type . "login")
         )
        ("example2"
         ("From" . "Your Name <account@example2.com>")
         (wl-smtp-posting-server . "smtp.example2.com")
         (wl-smtp-posting-user . "your account")
         (wl-smtp-posting-port . 587)
         (wl-smtp-connection-type . 'starttls)
         (wl-smtp-authenticate-type . "plain")
         )
        ("ssh:smtp"
         ;; need ssh tunnel
         ;; ssh -f -N -L 20025:localhost:25 smtp.server.com
         ("From" . "Your Name <account@example3.com>")
         (wl-smtp-posting-server . "localhost")
         (wl-smtp-posting-user . "your ssh account")
         (wl-smtp-posting-port . 20025)
         (wl-smtp-connection-type . 'nil)
         (wl-smtp-authenticate-type . 'nil)
         )
        ))

ssh tunnel を自動的にやる事はできないモンだろうか (送信時に open して, 送信後に close する, みたいなの).

ついでに template の切り替えに関して幾つか設定.

;; template 切り替え時に 内容を表示
(setq wl-template-visible-select t)

draft-modeC-c C-n をするとテンプレートを切り替え

;; (define-key wl-draft-mode-map "\C-c\C-n" 'wl-template-select)

from に応じて wl-from, wl-envelope-from, 送信 smtp サーバを変更する送信時に変更

;; (add-hook 'wl-draft-send-hook
;;           (lambda ()
;;             (set (make-local-variable 'wl-from)
;;                  (std11-fetch-field "From"))))

送信時に自動的に wl-draft-config-alist を適用…しない?

;; (remove-hook 'wl-draft-send-hook 'wl-draft-config-exec)

基本設定

imap 関連

デフォルトの認証設定 フォルダ名は UTF-7 でエンコードされているので, 表示する際にこれをデコードする
(setq elmo-imap4-use-modified-utf7 t)

非同期チェック

(setq wl-folder-check-async t)

フォルダの位置の default からの変更

~/.cache/wl/ に集約している local の Mail folder(MH) の位置
;; (setq elmo-localdir-folder-path "~/.cache/wl/local")

local フォルダの設定: .lost+foundelmo-localdir-folder-path からの相対パスになっていることに注意

(setq elmo-lost+found-folder "+lost+found")
(setq wl-queue-folder "+queue")

folders の位置の変更

;; (setq wl-folders-file "~/.mu(concat my:d:password-store "/wl-folders.gpg"))

Drafts, Trash の置き場所

(setq wl-draft-folder "+drafts")
(setq wl-trash-folder "+trash")
(setq wl-temporary-file-directory "~/Downloads/")

アドレス帳

(setq wl-use-petname t)
;; (setq wl-address-file  "~/.mua/Address")

LDAP サーバからアドレスを引くことも可能. 以前は GCALDaemon を使って local に ldap サーバを上げていたのだけれども, Google Contacts の API が変わったらしく GCALDaemon で LDAP サーバは使えなくなったのでコメントアウト.

(setq wl-use-ldap t)
(setq wl-ldap-server "localhost")
(setq wl-ldap-port "389")
(setq wl-ldap-base "dc=math,dc=kyoto-u,dc=ac,dc=jp")

パスワードの保存先

;; (setq elmo-passwd-alist-file-name (concat my:d:password-store "/wl-passwd.gpg"))

フォルダ編集時に backup を作成しない.

(setq wl-fldmgr-make-backup nil)

FCC, BCC の設定

(setq wl-fcc nil)
;; (setq wl-fcc "%Sent")

fcc を既読にする場合は以下.=wl-fcc= が nil の場合には意味は無い

(setq wl-fcc-force-as-read t)

bcc は常に自身に.

(setq wl-bcc (concat user-mail-address))

起動時に %INBOX のみをチェック

(setq wl-auto-check-folder-name "%INBOX")

フォルダ選択時の初期設定

imap の namespace を毎度入力するのが面倒なので, これを追加しておく.
(setq wl-default-spec "%")

confirm 関連の設定

スキャン時の問い合わせの無効化. ちなみに confirm を nil にしても 問い合わせが無いだけで threshold は効くので, 明示的に nil に.
(setq elmo-folder-update-confirm nil)
(setq elmo-folder-update-threshold nil)
(setq elmo-message-fetch-confirm nil)
(setq elmo-message-fetch-threshold nil)
(setq wl-prefetch-confirm nil)
(setq wl-prefetch-threshold nil)

終了時に確認しない

(setq wl-interactive-exit nil)

送信時は確認する

(setq wl-interactive-send t)

dispose, delete の設定

Gmail用に%INBOXでは削除を wl-trash-folder への移動ではなく, 「delete」に.
(add-to-list 'wl-dispose-folder-alist
             '("^%INBOX" . delete))

迷惑メール関連も

(add-to-list 'wl-dispose-folder-alist
             '(".*Junk$" . delete))

折り返しの設定

message は折り返す.
(setq wl-message-truncate-lines nil)

draft も折り返す

(setq wl-draft-truncate-lines nil)

mode-line の設定

長いと嫌なのでイロイロ削る
(setq wl-summary-mode-line-format "") ; "%f {%t}(%n/%u/%a)"
(setq wl-message-mode-line-format "") ; "<< %f:%F>> [%m]"

SOMEDAY misc.

大きいメッセージを送信時に分割しない
(setq mime-edit-split-message nil)

スレッドは常に閉じる

(setq wl-thread-insert-opened nil)

3 pain 表示 -> 使わない

(setq wl-stay-folder-window nil)

未読を優先的に読む

(setq wl-summary-move-order 'unread)

改ページ無視

(setq wl-break-pages nil)

icon を使わない → GUI でもメニュー表示してないし, 体感的には遅くなる

(setq wl-highlight-folder-with-icon nil)

印刷 → ps-print-buffer に任せる←まだ保留. エラー吐きよる….

(setq wl-print-buffer-function 'ps-print-buffer)

キーバインド関連

C-c C-j を browse-url に明け渡す
(define-key wl-draft-mode-map "\C-c\C-j" 'browse-url-at-point)

M-u で unread にする

(define-key wl-summary-mode-map "\M-u" 'wl-summary-mark-as-unread)

i で sync <- Mew 風

(define-key wl-summary-mode-map "i" 'wl-summary-sync-update)

C-o は elscreen で使う

(define-key wl-summary-mode-map "\C-o" nil )

M-oauto-refile (Mew 風)

(define-key wl-summary-mode-map "\M-o" 'wl-summary-auto-refile)

flag とフォルダを行き来する関数の追加

”=” でフラグ付きフォルダと 実際にメッセージのあるフォルダを行き来する. Gmail の「スター付き」フォルダでも有効
(require 'elmo nil 'noerror)
(defun my:wl-summary-jump-to-referer-message ()
  (interactive)
  (when (wl-summary-message-number)
    (if (eq (elmo-folder-type-internal wl-summary-buffer-elmo-folder) 'flag)
        (progn
          (let* ((referer (elmo-flag-folder-referrer
                           wl-summary-buffer-elmo-folder
                           (wl-summary-message-number)))
                 (folder (if (> (length referer) 1)
                             (completing-read
                              (format "Jump to (%s): " (car (car referer)))
                              referer
                              nil t nil nil (car (car referer)))
                           (car (car referer)))))
            (wl-summary-goto-folder-subr folder 'no-sync nil nil t)
            (wl-summary-jump-to-msg (cdr (assoc folder referer)))))
      (when (eq (elmo-folder-type wl-summary-last-visited-folder) 'internal)
        (wl-summary-goto-last-visited-folder)))))
(define-key wl-summary-mode-map "=" 'my:wl-summary-jump-to-referer-message)

summary-mode の表示のカスタマイズ

自分が差出人である mail は To:某 と表示

(setq wl-summary-showto-folder-regexp ".*")
(setq wl-summary-from-function 'wl-summary-default-from)

サマリ行の表示関連

サマリ行のフォーマット指定
(setq wl-summary-line-format
      "%T%P%1@%1>%Y/%M/%D %21(%t%[%19(%c %f%)%]%) %#%~%s"
      wl-summary-width (- (window-width) 1))

サマリ表示は切り詰めない

(setq wl-subject-length-limit t)

スレッドの幅の指定

(setq wl-thread-indent-level 2)
(setq wl-thread-have-younger-brother-str "+" ;; "├" ;; "+"
      wl-thread-youngest-child-str "+" ;; "└" ;; "+"
      wl-thread-vertical-str "|";; "│" ;; "|"
      wl-thread-horizontal-str "-";; "─" ;; "-"
      wl-thread-space-str " ")

以下の二つの設定を有効にするには elmo-msgdb-extra-fields を設定する必要がある. この変数は振り分け判定にも使用するのでそこで設定している

Gmail 風に, 自分宛のメールに “>” をつけて表示する

元ネタ http://d.hatena.ne.jp/khiker/20080206/wanderlust
;; 一覧表示での置き換え規則に追加
(defun my:wl-summary-line-for-me ()
  (if (catch 'found
        (let ((to (elmo-message-entity-field wl-message-entity 'to))
              (cc (elmo-message-entity-field wl-message-entity 'cc)))
          (when (or (stringp to) cc)
            (setq to
                  (append (if (stringp to) (list to) to)
                          (when cc
                            (if (stringp cc) (list cc) cc)))))
          (dolist (i to)
            (when (wl-address-user-mail-address-p (eword-decode-string i))
              (throw 'found t)))))
      ">"
    ""))
;; > を summary-line-format に追加
(setq wl-summary-line-format-spec-alist
      (append wl-summary-line-format-spec-alist
              '((?> (my:wl-summary-line-for-me)))))

添付ファイルがあったら, サマリ行に @ を付ける

(setq wl-summary-line-format-spec-alist
      (append wl-summary-line-format-spec-alist
              '((?@ (wl-summary-line-attached)))))

クォートされた文字列もデコードする

昔はあれこれ設定してたけど, 今は良いのかな? とりあえず, デコードする長さを default の 1000 から二桁増やしておく.
(setq mime-field-decoding-max-size 100000)
;; (setq mime-header-lexical-analyzer
;;       '(
;;         eword-analyze-quoted-string
;;         eword-analyze-domain-literal
;;         eword-analyze-comment
;;         eword-analyze-spaces
;;         eword-analyze-special
;;         eword-analyze-encoded-word
;;         eword-analyze-atom))
(with-eval-after-load 'eword-decode
  (mime-set-field-decoder
   'From nil 'eword-decode-and-unfold-unstructured-field-body)
  (mime-set-field-decoder
   'CC nil 'eword-decode-and-unfold-unstructured-field-body)
  (mime-set-field-decoder
   'To nil 'eword-decode-and-unfold-unstructured-field-body))

日本語添付ファイルの処理

(leaf mime
  :config
  '(defadvice mime-entity-filename
       (after eword-decode-for-broken-MUA activate)
     "Decode eworded file name for *BROKEN* MUA."
     (when (stringp ad-return-value)
       (setq ad-return-value (eword-decode-string ad-return-value t))))
  )
;;
(use-package std11
  :config
  '(defadvice std11-wrap-as-quoted-string (before encode-string activate)
     "Encode a string."
     (use-package eword-encode
       (ad-set-arg 0 (or (eword-encode-string (ad-get-arg 0)) "" ))
       )
     ))

Subject が変わってもスレッドを切らない

(setq wl-summary-divide-thread-when-subject-changed nil)

Subject での Tab や複数スペースを無視

;; (defadvice std11-unfold-string (after simply activate)
;;   (setq ad-return-value
;;         (replace-regexp-in-string ad-return-value "[ \t]+" " ")))

重複メッセージを非表示に

フォルダ内の Message-ID が同じメールを非表示にする
(setq wl-folder-process-duplicates-alist
      '(
        (".*" . hide)
        ))

sort 順: 返信が来た順

元ネタは Re: wanderlust で GMail 風、新着レス順にソート.
(defun wl-summary-overview-entity-compare-by-reply-date (a b)
  "Compare message A and B by latest date of replies including thread."
  (cl-letf (((symbol-function 'string-max2)
             (lambda (x y) (cond ((string< x y) y) ;;>
                                 ('t x))))
            ((symbol-function 'elmo-entity-to-number)
             (lambda (x) (elt (cddr x) 0)))

            ((symbol-function 'thread-number-get-date)
             (lambda (x) (timezone-make-date-sortable
                          (elmo-msgdb-overview-entity-get-date
                           (elmo-message-entity
                            wl-summary-buffer-elmo-folder
                            x)))))
            ((symbol-function 'thread-get-family)
             (lambda (x)
               (cons x (wl-thread-entity-get-descendant
                        (wl-thread-get-entity x)))))
            ((symbol-function 'max-reply-date)
             (lambda (x) (cond ((eq 'nil x)
                                'nil)
                               ((eq 'nil (cdr x))
                                (thread-number-get-date (car x)))
                               ('t
                                (string-max2 (thread-number-get-date (car x))
                                             (max-reply-date (cdr x)))))))
            )
    (string<  ;;>
     (max-reply-date (thread-get-family (elmo-entity-to-number a)))
     (max-reply-date (thread-get-family (elmo-entity-to-number b))))))
;; (defun wl-summary-overview-entity-compare-by-reply-date (a b)
;;   "Compare message A and B by latest date of replies including thread."
;;   (flet ((string-max2 (x y) (cond ((string< x y) y) ;;>
;;                                   ('t x)))
;;          (elmo-entity-to-number (x)
;;                                 (elt (cddr x) 0))
;;          (thread-number-get-date (x)
;;                                  (timezone-make-date-sortable
;;                                   (elmo-msgdb-overview-entity-get-date
;;                                    (elmo-message-entity
;;                                     wl-summary-buffer-elmo-folder
;;                                     x))))
;;          (thread-get-family (x)
;;                             (cons x (wl-thread-entity-get-descendant
;;                                      (wl-thread-get-entity x))))
;;          (max-reply-date  (x)
;;                           (cond ((eq 'nil x)
;;                                  'nil)
;;                                 ((eq 'nil (cdr x))
;;                                  (thread-number-get-date (car x)))
;;                                 ('t
;;                                  (string-max2 (thread-number-get-date (car x))
;;                                               (max-reply-date (cdr x))))))
;;          )
;;         (string<  ;;>
;;          (max-reply-date (thread-get-family (elmo-entity-to-number a)))
;;          (max-reply-date (thread-get-family (elmo-entity-to-number b))))))
(add-to-list 'wl-summary-sort-specs 'reply-date)
(setq wl-summary-default-sort-spec 'reply-date)

振り分け設定

$ 以外を振り分け対象に
(setq wl-summary-auto-refile-skip-marks '("$"))

振り分け判定に使用するヘッダ

添付の有無の表示にも使うので Content-Type も登録. あと Delivered-To はメールの検索の時に結構重宝している.
(setq elmo-msgdb-extra-fields
      '(
        "List-Post"
        "List-Id"
        "List-ID"                  ;; たまに List-ID で来るメールあるよね?
        "Resent-CC"
        "Mailing-List"
        "X-Mailing-List"
        "X-ML-Address"
        "X-ML-Name"
        "X-ML-To"
        "X-Loop"
        "Delivered-To"
        "Content-Type"              ;; 添付の有無の表示の為に追加
        "X-Google-Appengine-App-Id" ;; GAEの送信するメールの振り分け用
        "To"
        "Cc"
        "From"
        "Subject"
        "Reply-To"
        "Auto-Submitted"            ;; Git commit/Cron notify
        ))

メッセージ表示

いったん全て非表示に

(setq wl-message-ignored-field-list '("^.*:"))

見たいヘッダだけ表示

(setq wl-message-visible-field-list
      '("^Subject:"
        "^From:"
        "^To:"
        "^Cc:"
        "^Date:"
        "^Message-ID:"
        ))

表示順の変更

Mew 風…
(setq wl-message-sort-field-list
      '("^Subject:"
        "^From:"
        "^To:"
        "^Cc:"
        "^Date:"
        "^Message-ID:"
        ))

From, To を省略表示しない

To や From にアドレスが沢山指定されていると省略されるので, これを無効化
(setq wl-message-use-header-narrowing nil)

Wanderlust: Face の設定

デフォルトより細かく指定するために幾つかの face 定義を追加.
(setq wl-highlight-message-header-alist
      '(("Subject[ \t]*:" . wl-highlight-message-subject-header-contents)
        ("From[ \t]*:" . wl-highlight-message-from-header-contents)
        ("Date[ \t]*:" . wl-highlight-message-date-header-contents)
        ("\\(.*To\\|Cc\\|Newsgroups\\)[ \t]*:" . wl-highlight-message-important-header-contents)
        ("\\(User-Agent\\|X-Mailer\\|X-Newsreader\\)[ \t]*:" . wl-highlight-message-unimportant-header-contents)
        ))
;; use modus-vivendi color pallete
(face-spec-set `wl-highlight-message-subject-header-contents     `((t (:foreground "#ff7f9f" :bold t ))))  ; red-cooler
(face-spec-set `wl-highlight-message-from-header-contents        `((t (:foreground "#fec43f" :bold t ))))  ; yellow-warmer
(face-spec-set `wl-highlight-message-date-header-contents        `((t (:foreground "#88ca9f" :bold t ))))  ; green-faint
(face-spec-set `wl-highlight-message-important-header-contents   `((t (:foreground "#ff66ff" ))))          ; magenta-intence
(face-spec-set `wl-highlight-message-unimportant-header-contents `((t (:foreground "#9f9f9f" ))))          ; fg-dim

以下, 元々定義されているfaceの設定

;; ;; face の色付け
;; ;;;###autoload
;; (defun my:wl-set-face (face spec)
;;   (make-face face)
;;   (cond ((fboundp 'face-spec-set)
;;          (face-spec-set face spec))
;;         (t
;;          (wl-declare-face face spec))))
;; (my:wl-set-face 'wl-highlight-folder-closed-face
;;                 '((t (:foreground "#4cff4c" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-folder-few-face
;;                 '((t (:foreground "#FF4C4C" :bold t :italic nil :weight bold ))))
;; (my:wl-set-face 'wl-highlight-folder-zero-face
;;                 '((t (:foreground "#f6f3e8" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-1
;;                 '((t (:foreground "#7fff7f" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-2
;;                 '((t (:foreground "#ffff7f" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-3
;;                 '((t (:foreground "#7f7fff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-4
;;                 '((t (:foreground "#7fffff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-5
;;                 '((t (:foreground "#ff7fff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-6
;;                 '((t (:foreground "#ff7f7f" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-7
;;                 '((t (:foreground "#4cff4c" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-8
;;                 '((t (:foreground "#ffff4c" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-9
;;                 '((t (:foreground "#4c4cff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-10
;;                 '((t (:foreground "#4cffff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-11
;;                 '((t (:foreground "#ff4cff" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-cited-text-12
;;                 '((t (:foreground "#ff4c4c" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-date-header-contents
;;                 '((t (:foreground "#4CFF4C" :bold t :italic nil :weight bold ))))
;; (my:wl-set-face 'wl-highlight-message-header-contents
;;                 '((t (:foreground "#aaaaaa" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-headers
;;                 '((t (:foreground "#4CFFFF" :bold t :italic nil :weight bold ))))
;; (my:wl-set-face 'wl-highlight-message-important-header-contents2
;;                 '((t (:foreground "#4CFF4C" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-signature
;;                 '((t (:foreground "#aaaaaa" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-message-important-header-contents
;;                 '((t (:foreground "#FF4CFF" :bold t :italic nil :weight bold ))))
;; (my:wl-set-face 'wl-highlight-message-subject-header-contents
;;                 '((t (:foreground "#FF4C4C" :bold t :italic nil :weight bold ))))
;; (my:wl-set-face 'wl-highlight-message-unimportant-header-contents
;;                 '((t (:foreground "#aaaaaa" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-summary-answered-face
;;                 '((t (:foreground "#4CFF4C" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-summary-refiled-face
;;                 '((t (:foreground "#7F7FFF" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-summary-thread-top-face
;;                 '((t (:foreground "#F6F3E8" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-summary-important-flag-face
;;                 '((t (:foreground "#ffff4c" :bold nil :italic nil :weight normal ))))
;; (my:wl-set-face 'wl-highlight-folder-killed-face
;;                 '((t (:foreground "#4c4c4c" :5Dbold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-many-face
;;                 '((t (:foreground "#ff7fbf" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-opened-face
;;                 '((t (:foreground "#4cffff" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-path-face
;;                 '((t (:underline t :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-unknown-face
;;                 '((t (:foreground "#4cffff" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-unread-face
;;                 '((t (:foreground "#4c4cff" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-header-separator-face
;;                 '((t (:inherit highlight :bold t ))))
;; (my:wl-set-face 'wl-highlight-message-citation-header
;;                 '((t (:foreground "#7fff7f" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-copied-face
;;                 '((t (:foreground "#4CFFFF" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-deleted-face
;;                 '((t (:foreground "#4c4c4c" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-displaying-face
;;                 '((t (:underline t :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-disposed-face
;;                 '((t (:foreground "#aaaaaa" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-flagged-face
;;                 '((t (:foreground "#ffff7f" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-forwarded-face
;;                 '((t (:foreground "#7f7fff" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-high-read-face
;;                 '((t (:foreground "#7fff7f" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-high-unread-face
;;                 '((t (:foreground "#ffb347" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-important-face
;;                 '((t (:foreground "#ffff4c" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-killed-face
;;                 '((t (:foreground "#4c4c4c" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-l:read-face
;;                 '((t (:foreground "#4CFF4C" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-l:unread-face
;;                 '((t (:foreground "#7fbfff" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-new-face
;;                 '((t (:foreground "#ff4c4c" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-normal-face
;;                 '((t (:foreground "#f6f3e8" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-prefetch-face
;;                 '((t (:foreground "#4c4cee" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-resend-face
;;                 '((t (:foreground "#ffb347" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-target-face
;;                 '((t (:foreground "#4CFFFF" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-temp-face
;;                 '((t (:foreground "##ee4cee" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-unread-face
;;                 '((t (:foreground "#ff4c4c" :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-thread-indent-face
;;                 '((t (:underline t :bold nil :italic nil ))))

作成/返信設定

自分宛のメールに返信する場合は To:, Cc: から自分のアドレスを削除
(setq wl-draft-always-delete-myself t)

“a” (without-argument)では Reply-To:From: などで 指定された唯一人または唯一つの投稿先に返信. また, X-ML-Name:Reply-To: がついているなら Reply-To: 宛に返信

;; (setq wl-draft-reply-without-argument-list
;;       '(
;;         (("X-ML-Name" "Reply-To") . (("Reply-To") nil nil))
;;         ("X-ML-Name" . (("To" "Cc") nil nil))
;;         ("Followup-To" . (nil nil ("Followup-To")))
;;         ("Newsgroups" . (nil nil ("Newsgroups")))
;;         ("Reply-To" . (("Reply-To") nil nil))
;;         ("Mail-Reply-To" . (("Mail-Reply-To") nil nil))
;;         ("From" . (("From") nil nil)))
;;       )

C-u a (with-argument)であれば関係する全ての人・投稿先に返信

;; (setq wl-draft-reply-with-argument-list
;;       '(
;;         (("X-ML-Name" "Reply-To") . (("Reply-To") nil nil))
;;         ("X-ML-Name" . (("To" "Cc") nil nil))
;;         ("Followup-To" . (nil nil ("Followup-To")))
;;         ("Newsgroups" . (nil nil ("Newsgroups")))
;;         ("Reply-To" . (("Reply-To") nil nil))
;;         ("Mail-Reply-To" . (("Mail-Reply-To") nil nil))
;;         ("From" . (("From") nil nil)))
;;       )

サマリ表示には petname を使うが, 引用には使わない

(setq wl-default-draft-cite-decorate-author nil)

ドラフトの自動保存の無効化

偶に暴発している様な…? elscreen のせいかしら.
;; (setq wl-auto-save-drafts-interval 300)
(setq wl-auto-save-drafts-interval nil)

メール本文の文字コード

丸囲み数字なんかが入ってしまうと 勝手にエンコーディングが変わってしまって鬱陶しい. どうしたモンだろうかね.
(add-hook 'wl-draft-mode-hook
          (lambda ()
            (add-to-list 'mime-charset-type-list '(utf-8 8 nil))))

draft mode で orgtbl を有効に

(add-hook 'wl-draft-mode-hook
          (lambda ()
            (progn
              (require 'org-table)
              #'turn-on-orgtbl)))

c-sig

署名の選択に c-sig を使用している. 設定は以下の通り. Mew 風に C-c <tab> で signature を挿入するようにしている
(leaf c-sig
  :commands insert-signature-eref
  :config
  (eval-when-compile (require 'wl))
  (setq sig-insert-end t
        sig-save-to-sig-name-alist nil
        message-signature-file nil)
  )
(define-key wl-draft-mode-map "\C-c\t" 'insert-signature-eref)
(add-hook 'wl-draft-mode-hook
          (lambda ()
            (define-key (current-local-map) "\C-c\C-w"
              'insert-signature-eref)))

GPG 署名

以前は mailcrypt を使っていたけれど, epa があるので主にキーバインドの設定のみ. draft-mode の文字コードをあらかじめ指定しておかないと, 送信時に文字コードが変換されるので不正な署名となってしまう.

もっとうまい方法/正攻法がありそうな気がするけれど, 使えてるから, まあ良いかな, とか.

(setq mime-pgp-verify-when-preview nil)
(defun my:epa-wl-decrypt-message ()
  (interactive)
  (save-window-excursion
    (wl-summary-jump-to-current-message)
    (wl-message-decrypt-pgp-nonmime)))
(defun my:epa-wl-verify-message ()
  (interactive)
  (save-selected-window
    (wl-summary-jump-to-current-message)
    (wl-message-verify-pgp-nonmime)))
(leaf-keys ((:wl-summary-mode-map
             ("C-c : d" . my:epa-wl-decrypt-message)
             ("C-c : v" . my:epa-wl-verify-message))
            (:wl-draft-mode-map
             ("C-c : s" . epa-mail-sign)
             ("C-c : e" . epa-mail-encrypt)))
           )

検索

notmuchを使う.
(leaf elmo-search
  :config
  (elmo-search-register-engine 'notmuch-custom 'local-file
                               :prog "notmuch-query-custom"
                               :args '(elmo-search-split-pattern-list)
                               :charset 'utf-8)
  (setq elmo-search-default-engine 'notmuch-custom))
(leaf wl-qs
  :config
  (setq wl-quicksearch-folder "[]"
        )
  )
(leaf-keys ((:wl-summary-mode-map
             ("v" . wl-quicksearch-goto-search-folder-wrapper))
            (:wl-folder-mode-map
             ("v" . wl-quicksearch-goto-search-folder-wrapper)))
           )

実際の呼び出しはスレッドを全部取得したいので以下を呼び出している

#!/bin/sh
if [ ! x"$*" = x"" ] ; then
    res=$(notmuch search --output=threads "$*")
fi
if [ ! x"$res" = x"" ] ; then
    echo $res | xargs notmuch search --sort=oldest-first --output=files
fi

検索時にメールが多すぎると怒られるので. 数字は適当.

(setq elmo-multi-divide-number 2000000
      elmo-multi-number 2000000)

Linux Desktop で mailto: リンクを扱うために

ついでに mailto のリンクを emacsclient で扱うために, 以下の関数を定義しておく
;;;###autoload
(defun my:mailto-compose-mail (mailto-url)
  "Custom: handling mailto: link"
  (if (and (stringp mailto-url)
           (string-match "\\`mailto:" mailto-url))
      (progn
        (eval-and-compile (require 'rfc6068))
        (let* ((headers (mapcar (lambda (h) (cons (intern (car h)) (cdr h)))
                                (rfc6068-parse-mailto-url mailto-url)))
               (good-headers (cl-remove-if (lambda (h) (member (car h) '(Body))) headers))
               (body (cdr (assoc 'Body headers))))
          (wl-draft good-headers nil nil body)))))

Desktop の設定では

#!/bin/sh
# emacs-mailto-handler

mailto=$1
mailto="mailto:${mailto#mailto:}"
mailto=$(printf '%s\n' "$mailto" | sed -e 's/[\"]/\\&/g')
elisp_expr="(my:mailto-compose-mail \"$mailto\")"

emacsclient -a "" -n --eval "$elisp_expr" \
            '(set-window-dedicated-p (selected-window) t)'

をメーラとして指定すれば良い. GNOME は .desktop ファイルが無いと「お気に入り」登録ができないので 以下のファイルを適当な名前で ~/.local/share/applications/ 以下に放り込んでおくと良いだろう

[Desktop Entry]
Name=Emacs Mail Handler
GenericName=Mail User Agent
X-GNOME-FullName=Emacs Mail Handler
Comment=Use emacsclient as MUA, handling mailto link
Keywords=email
Exec=/home/uwabami/bin/emacs-mailto-handler %U
Icon=emacs25
Terminal=false
Type=Application
Categories=GNOME;GTK;Office;Email;
StartupNotify=false
MimeType=application/mbox;message/rfc822;x-scheme-handler/mailto;

mbsync

(leaf mbsync
  :if (executable-find "mbsync")
  :ensure t
  )

補完: vertico, marginalia, consult, corfu

最近話題になりだしたので, ちょっと使い始めてみた.

helm, ivy の無効化

依存する拡張がまだまだ多いので, 一度インストールして邪魔しないようにしておくことに.
(leaf helm :defer-config (helm-mode -1))
(leaf ivy :defer-config (ivy-mode -1))

無視する拡張子の追加設定

とりあえず, 無視するファイルの拡張子を指定しておく.
(leaf *completion
  :init
  ;; 補完で無視する拡張子の追加.そのうち増える.
  (cl-loop for ext in
           '(;; TeX
             ".dvi"
             ".fdb_latexmk"
             ".fls"
             ".ilg"
             ".jqz"
             ".nav"
             ".out"
             ".snm"
             ".synctex\\.gz"
             ".vrb"
             ;; fortran >= 90
             ".mod"
             ;; zsh
             ".zwc"
             ;; libtool
             ".in"
             ".libs/"
             ;; fxxkin Apple
             ".DS_Store"
             "._DS_Store"
             ;; "org-id-locations"
             )
           do (add-to-list 'completion-ignored-extensions ext))
  )

vertico: 本体

find-fileでHelmみたいにC-lでディレクトリを遡る - emacs より, C-l で一つ上の階層へ上がれる様にしたり.
(leaf vertico
  :ensure t
  :preface
  (defun my:disable-selection ()
    (when (eq minibuffer-completion-table #'org-tags-completion-function)
      (setq-local vertico-map minibuffer-local-completion-map
                  completion-cycle-threshold nil
                  completion-styles '(basic))))
  ;;
  ;; @see https://misohena.jp/blog/2022-08-15-transition-ivy-to-vertico.html
  ;; 候補更新時に最初の候補を選択しない -> (vertico-preselect . 'prompt)

  ;; ただし require-matchがt(やそれに類するもの)で入力が空ではなくマッチする候
  ;; 補がある場合は、その候補の先頭を選択する。
  (defun my:vertico--recompute (orig-fun pt content &rest args)
    (let ((result (apply orig-fun pt content args)))
      (if (and (not (equal content ""))
               ;; 入力が空の時は(require-matchであっても)defaultまたはnilを返
               ;; すことになっている。
               (> (alist-get 'vertico--total result) 0) ;; < for syntax
               ;; completing-readの説明によれば
               ;; nil,confirm,confirm-after-completion以外はtのように
               ;; 振る舞うべき。
               (not (memq minibuffer--require-match
                          '(nil confirm confirm-after-completion))))
          (setf (alist-get 'vertico--index result) 0))
      result))
  ;;
  ;;
  (defun my:filename-upto-parent ()
    "Move to parent directory like \"cd ..\" in find-file."
    (interactive)
    (let ((sep (eval-when-compile (regexp-opt '("/" "\\")))))
      (save-excursion
        (left-char 1)
        (when (looking-at-p sep)
          (delete-char 1)))
      (save-match-data
        (when (search-backward-regexp sep nil t)
          (right-char 1)
          (filter-buffer-substring (point)
                                   (save-excursion (end-of-line) (point))
                                   #'delete)))))
  :advice
  ((:before vertico--setup
            my:disable-selection)
   (:around vertico--recompute-candidates
            my:vertico--recompute)
   )
  :bind
  (:vertico-map (("C-l" . my:filename-upto-parent)))
;;  :custom-face
;;  `((vertico-current
;;     . '((t (:inherit hl-line :background unspecified)))))
  :init
  (setq vertico-count 8
        vertico-cycle t
        vertico-preselect 'prompt
        vertico-multiline '(("" 0 1 (face vertico-multiline))
                            ("" 0 1 (face vertico-multiline)))
        )
  ;; :config
  :hook (emacs-startup-hook . vertico-mode)
  )

marginalia: リッチな注釈(Enable richer annotations)

行揃えが微妙. あと, ファイル名を省略表示できないのかな? ⇒ Better truncation method for file names #70
(leaf marginalia
  :ensure t
  :init
  ;; 補完でも icon 表示
  (leaf nerd-icons-completion
   :ensure t
   :hook
   (emacs-startup-hook . nerd-icons-completion-mode)
   )
  :bind (("M-A" . marginalia-cycle)
         (:minibuffer-local-map
          ("M-A" . marginalia-cycle)
          ))
  :init
  (setq marginalia-annotators    '(marginalia-annotators-light
                                   marginalia-annotators-heavy
                                   nil)
        marginalia-align         'right
        marginalia-align-offset  -2 ;; icon 分引いておく
        )
  :hook
  ((emacs-startup-hook . marginalia-mode)
   (marginalia-mode-hook . nerd-icons-completion-marginalia-setup)
   )
  )

consult: 便利コマンド集

とりあえず recetnf が使えないと途方に暮れるので consult-recent-file のカスタマイズのみ.
(leaf consult
  :ensure t
  :bind (("C-x C-r" . my:consult-recent-file))
  ;; :defvar recentf-list
  :init
  (setq consult-narrow-key          "<"
        consult-async-refresh-delay 0.2
        consult-preview-key         nil
        ;; 増やさないと preview 時に theme がロードされない模様.
        ;; とりあえず default の 10 倍にしている. 1 MB かな?
        consult-preview-raw-size 1024000
        )
  :config
  (defun my:consult-recent-file ()
    "Find recent using `completing-read' with shorten filename"
    (interactive)
    (eval-when-compile (require 'recentf))
    (recentf-mode +1)
    (let ((files (mapcar (lambda (f)
                           (cons (my:shorten-file-path f (- (window-width) 2)) f))
                         recentf-list)))
      (let ((selected
             (consult--read (mapcar #'car files)
                            :prompt "Find recent file: "
                            :sort nil
                            :require-match t
                            :category 'file
                            :state (consult--file-preview)
                            :history 'file-name-history)))
        (find-file (assoc-default selected files)))))
  )

cape: 補完 backend

(leaf cape
  :disabled t
  :ensure t
  :config
  (setq cape-dabbrev-check-other-buffers nil)
  (add-to-list 'completion-at-point-functions 'cape-keyword)
  (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-tex)
  (add-to-list 'completion-at-point-functions 'cape-emoji)
  )

orderless: 補完候補の選択

イロイロと凝れそうだけど, とりあえずはデフォルトのままで.
(leaf orderless
  :ensure t
  :init
  (setq completion-styles '(orderless)
        orderless-matching-styles '(orderless-prefixes
                                    orderless-flex
                                    orderless-regexp
                                    orderless-initialism
                                    orderless-literal)
        )
  )

kind-icon: corfu で補完候補にアイコン表示

svg-lib でのアイコン表示ではなく nerd-icons を利用するように.
(leaf kind-icon
  :ensure t
  :after corfu-terminal
  :init
  (setq kind-icon-default-face 'corfu-default
        kind-icon-use-icons    nil
        kind-icon-mapping      `(
                                 (array          ,(nerd-icons-codicon "nf-cod-symbol_array")       :face font-lock-type-face)
                                 (boolean        ,(nerd-icons-codicon "nf-cod-symbol_boolean")     :face font-lock-builtin-face)
                                 (class          ,(nerd-icons-codicon "nf-cod-symbol_class")       :face font-lock-type-face)
                                 (color          ,(nerd-icons-codicon "nf-cod-symbol_color")       :face success)
                                 (command        ,(nerd-icons-codicon "nf-cod-terminal")           :face default)
                                 (constant       ,(nerd-icons-codicon "nf-cod-symbol_constant")    :face font-lock-constant-face)
                                 (constructor    ,(nerd-icons-codicon "nf-cod-triangle_right")     :face font-lock-function-name-face)
                                 (enummember     ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
                                 (enum-member    ,(nerd-icons-codicon "nf-cod-symbol_enum_member") :face font-lock-builtin-face)
                                 (enum           ,(nerd-icons-codicon "nf-cod-symbol_enum")        :face font-lock-builtin-face)
                                 (event          ,(nerd-icons-codicon "nf-cod-symbol_event")       :face font-lock-warning-face)
                                 (field          ,(nerd-icons-codicon "nf-cod-symbol_field")       :face font-lock-variable-name-face)
                                 (file           ,(nerd-icons-codicon "nf-cod-symbol_file")        :face font-lock-string-face)
                                 (folder         ,(nerd-icons-codicon "nf-cod-folder")             :face font-lock-doc-face)
                                 (interface      ,(nerd-icons-codicon "nf-cod-symbol_interface")   :face font-lock-type-face)
                                 (keyword        ,(nerd-icons-codicon "nf-cod-symbol_keyword")     :face font-lock-keyword-face)
                                 (macro          ,(nerd-icons-codicon "nf-cod-symbol_misc")        :face font-lock-keyword-face)
                                 (magic          ,(nerd-icons-codicon "nf-cod-wand")               :face font-lock-builtin-face)
                                 (method         ,(nerd-icons-codicon "nf-cod-symbol_method")      :face font-lock-function-name-face)
                                 (function       ,(nerd-icons-codicon "nf-cod-symbol_method")      :face font-lock-function-name-face)
                                 (module         ,(nerd-icons-codicon "nf-cod-file_submodule")     :face font-lock-preprocessor-face)
                                 (numeric        ,(nerd-icons-codicon "nf-cod-symbol_numeric")     :face font-lock-builtin-face)
                                 (operator       ,(nerd-icons-codicon "nf-cod-symbol_operator")    :face font-lock-comment-delimiter-face)
                                 (param          ,(nerd-icons-codicon "nf-cod-symbol_parameter")   :face default)
                                 (property       ,(nerd-icons-codicon "nf-cod-symbol_property")    :face font-lock-variable-name-face)
                                 (reference      ,(nerd-icons-codicon "nf-cod-references")         :face font-lock-variable-name-face)
                                 (snippet        ,(nerd-icons-codicon "nf-cod-symbol_snippet")     :face font-lock-string-face)
                                 (string         ,(nerd-icons-codicon "nf-cod-symbol_string")      :face font-lock-string-face)
                                 (struct         ,(nerd-icons-codicon "nf-cod-symbol_structure")   :face font-lock-variable-name-face)
                                 (text           ,(nerd-icons-codicon "nf-cod-text_size")          :face font-lock-doc-face)
                                 (typeparameter  ,(nerd-icons-codicon "nf-cod-list_unordered")     :face font-lock-type-face)
                                 (type-parameter ,(nerd-icons-codicon "nf-cod-list_unordered")     :face font-lock-type-face)
                                 (unit           ,(nerd-icons-codicon "nf-cod-symbol_ruler")       :face font-lock-constant-face)
                                 (value          ,(nerd-icons-codicon "nf-cod-symbol_field")       :face font-lock-builtin-face)
                                 (variable       ,(nerd-icons-codicon "nf-cod-symbol_variable")    :face font-lock-variable-name-face)
                                 (t              ,(nerd-icons-codicon "nf-cod-code")               :face font-lock-warning-face))
        )
  :config
  (add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)
  )

corfu: on the fly completions

company より使い勝手が良い, 気がする.Corfuの自動補完で候補の存在を伝える事と候補を選べるようにする事を分離する を参考に.
(leaf corfu
  :disabled t
  :ensure t
  :preface
  (defvar-keymap my:corfu-auto-map
    :doc "Keymap used when popup is shown automatically."
    "C-g" #'corfu-quit
    )
  (defvar my:corfu-in-auto-complete nil)
;;;###autoload
(defun my:corfu--auto-complete-deferred:around (oldfun &rest args)
  ;; 自動補完時に呼び出される
  (let ((my:corfu-in-auto-complete t))
    ;; 元の関数を呼び出す. 補完候補があるなら setup が呼ばれる
    (apply oldfun args)))
;;;###autoload
(defun my:corfu--setup:around (oldfun &rest args)
  (if my:corfu-in-auto-complete
      ;; 自動補完の時
      (progn
        (setf ;; (alist-get 'alpha corfu--frame-parameters) 90
              (alist-get 'tab-line-format corfu--buffer-parameters) "   C-M-i:補完"
              ;; 最初の候補を選ばない
              corfu-preselect 'prompt)
        (let (;; キー割り当てを極力無くす
              (corfu-map my:corfu-auto-map))
          (apply oldfun args)))
    ;; 手動補完の時
    (setf ;; (alist-get 'alpha corfu--frame-parameters) 100
          (alist-get 'tab-line-format corfu--buffer-parameters) nil
          ;; 最初の候補を選ぶ
          corfu-preselect 'first)
    (apply oldfun args)))
  :advice ((:around corfu--auto-complete-deferred
                    my:corfu--auto-complete-deferred:around)
           (:around corfu--setup
                    my:corfu--setup:around)
           )
  :init
  (leaf corfu-terminal
    :ensure t
    :config
    (setq corfu-terminal-disable-on-gui nil)
    )
  :bind
  (:corfu-map
   ("TAB"       . corfu-next)
   ("<tab>"     . corfu-next)
   ("S-TAB"     . corfu-previous)
   ("<backtab>" . corfu-previous)
   ("C-j"       . corfu-next)
   ("C-n"       . corfu-next)
   ("C-p"       . corfu-previous)
   ("C-k"       . corfu-previous)
   ("C-g"       . corfu-quit)
   )
  :hook (emacs-startup-hook . (lambda ()
                                (corfu-terminal-mode +1)
                                (global-corfu-mode)))
  :config
  (setq completion-cycle-threshold 4
        tab-always-indent          'complete
        corfu-cycle                t
        corfu-auto                 t
        corfu-preselect            'prompt
    )
  )

翻訳

DeepL 翻訳

ブラウザ呼び出し
(leaf *deepl-translate
  :commands my:deepl-translate
  :bind (("C-x T" . my:deepl-translate))
  :preface
  (require 'url-util)
  (defun my:deepl-translate (&optional string)
    (interactive)
    (setq string
          (cond ((stringp string) string)
                ((use-region-p)
                 (buffer-substring (region-beginning) (region-end)))
                (t
                 (save-excursion
                   (let (s)
                     (forward-char 1)
                     (backward-sentence)
                     (setq s (point))
                     (forward-sentence)
                     (buffer-substring s (point)))))))
    (run-at-time 0.1 nil 'deactivate-mark)
    (browse-url
     (concat
      "https://www.deepl.com/translator#en/ja/"
      (url-hexify-string string)
      )))
  )

Google 翻訳

(leaf google-translate
  :ensure t
  :defvar google-translate-backend-method
  :init
  (defvar google-translate-translation-directions-alist '(("en" . "ja") ("ja" . "en")))
  (leaf popup :ensure t)
  (defun my:google-translate--search-tkk ()
    "Search TKK. @see https://github.com/atykhonov/google-translate/issues/52"
    (list 430675 2721866130))
  :bind
  ("C-x t" . google-translate-smooth-translate)
  :advice (:override google-translate--search-tkk
                     my:google-translate--search-tkk)
  :config
  (setq google-translate-translation-directions-alist '(("en" . "ja") ("ja" . "en"))
        google-translate-backend-method 'curl)
  )

校正, 辞書等

ispell,=flyspell=: spell checker, on-the-fly spell checker

ispell はコマンドとして aspell を利用する.
(leaf flyspell
  :if (executable-find "aspell")
  :init
  (setq ispell-program-name   "aspell"
        ispell-check-comments nil
        ispell-skip-html      t
        ispell-silently-savep t
        )
  :bind (:flyspell-mode-map
         ("C-." . nil)
         ("C-," . nil))
  :defer-config
  (add-to-list 'ispell-skip-region-alist '("[^\000-\377]+"))
  )

lookup: 電子辞書の検索

EPWING化した辞書群を検索するために lookup-el ver. 1.4 系列を利用
(leaf lookup
  :if (and (file-exists-p "/etc/emacs/site-start.d/50lookup-el.el")
           (file-exists-p "/usr/local/share/dict/lookup-enabled"))
  :commands (lookup lookup-region lookup-pattern)
  :bind (("C-c w" . lookup-pattern)
         ("C-c W" . lookup-word))
  :init
  (setq lookup-search-agents '((ndeb "/usr/local/share/dict/eijiro" :alias "英辞郎")
                               (ndeb "/usr/local/share/dict/waeijiro" :alias "和英辞郎")
                               (ndeb "/usr/local/share/dict/rikagaku5" :alias "理化学辞典 第5版")
                               (ndeb "/usr/local/share/dict/koujien4" :alias "広辞苑 第4版")
                               (ndeb "/usr/local/share/dict/wadai5" :alias "研究社 和英大辞典 第5版")
                               ;; (ndeb "/usr/local/share/dict/eidai6" :alias "研究社 英和大辞典 第6版")
                               ;; (ndeb "/usr/local/share/dict/colloc" :alias "研究社 英和活用大辞典 ")
                               )
        )
  )

text-adjust: 全角文字の撲滅

(leaf text-adjust :vc (:url "https://github.com/uwabami/text-adjust.el"))

プログラミング関連

Language Server による補完: eglot

Emacs 29から 本体に同梱されることになったので eglot を利用することに. サーバの呼び出し自体は言語毎に hook で設定する.
(leaf eglot
  :ensure t
  :bind (("M-t" . xref-find-definitions)
         ("M-r" . xref-find-references))
;;  :init
;;;###autoload
;; (defun my:eglot-capf()
;;   (setq-local completion-at-point-functions
;;               (list (cape-super-capf
;;                      #'eglot-completion-at-point)
;;                     #'cape-keyword
;;                     #'cape-dabbrev
;;                     #'cape-file)
;;               ))
;;  :hook
;;  ((eglot-managed-mode-hook . my:eglot-capf))
  :config
  ;; digestif は日本語TeXとイマイチ相性が悪い(上手く使い熟せていない...).
  (delete (assoc '(tex-mode context-mode texinfo-mode bibtex-mode)
                 eglot-server-programs)
          eglot-server-programs)
  (add-to-list 'eglot-server-programs
             '((latex-mode tex-mode context-mode texinfo-mode bibtex-mode) . ("texlab")))
  )

flaymake

(leaf flymake
  :ensure t
  :bind (:flymake-mode-map
         ("C-c C-p" . flymake-goto-prev-error)
         ("C-c C-n" . flymake-goto-next-error)
         )
  )
(leaf flymake-diagnostic-at-point
  :ensure t
  :after flymake
  :hook (flymake-mode-hook . flymake-diagnostic-at-point-mode)
  )

TeX: AUCTeX

やっている事は
  • japanese-latex-mode において, 幾つかのコマンドが追加/上書きされているものの, あまり使うことの無いコマンドが表示されるのが嫌なのでそれらを削除.
  • コンパイルにはLatexmkを使う

と言った所. …ただ, Beamerのクラスなんかだと勝手にdefault コマンドが LaTeX に変更されたりするので挙動がイマイチ良くわからん. async で latexmk -pvc を走らせておいた方が気持が良い, 気がしている.

(leaf tex-site
  :if (and (executable-find "uplatex")
           (executable-find "latexmk"))
  :ensure auctex
  :preface
  (unless (file-directory-p (expand-file-name "auctex/auto" my:d:tmp))
    (progn
      (make-directory (expand-file-name "auctex/auto" my:d:tmp) t)
      (make-directory (expand-file-name "auctex/style" my:d:tmp) t)))
  ;;
  :mode ("\\.tex\\'" . japanese-latex-mode) ;; → tex-jp で提供
  ;;
  :init
  (leaf auctex-latexmk
    :ensure t
    :hook
    ((LaTeX-mode-hook . auctex-latexmk-setup))
    )
  (leaf tex-jp
    :commands japanese-latex-mode
    :hook
    `((LaTeX-mode-hook . (lambda()
                           (turn-on-reftex)
                           (TeX-source-correlate-mode)
                           (TeX-PDF-mode)
                           (LaTeX-math-mode)
                           (outline-minor-mode)
                           (setq TeX-command-default "LatexMk"
                                 japanese-LaTeX-command-default "LatexMk")
                           ))
      (LaTeX-mode-hook . (lambda()
                           (add-hook 'TeX-after-compilation-finished-functions
                                     #'TeX-revert-document-buffer)))
      (LaTeX-mode-hook . eglot-ensure)
      )
    )
  :config
  ;; reftex
  (setq reftex-plug-into-AUCTeX t
        reftex-cite-prompt-optional-args t
        )
  ;; tex-site
  (setq TeX-default-mode 'japanese-latex-mode ;; 🤔
        TeX-force-default-mode t
        TeX-parse-self t
        TeX-auto-save t
        TeX-auto-local (expand-file-name "auctex/auto" my:d:tmp)
        TeX-style-local (expand-file-name "auctex/style" my:d:tmp)
        TeX-auto-untabify t
        TeX-engine 'uptex
        TeX-view-program-selection  '((output-dvi "xdvi")
                                      ;; (output-pdf "Okular")
                                      (output-pdf "Atril")
                                      ;; (output-pdf "Evince")
                                      (output-html "xdg-open"))
        TeX-source-correlate-method 'synctex
        TeX-source-correlate-start-server t
        TeX-source-correlate-mode t
        ;; TeX-command-default "LatexMk"
        TeX-ispell-extend-skip-list t
        TeX-command-output-list '(("LatexMk" ("pdf")))
        TeX-electric-sub-and-superscript t
        )
  ;; tex-jp
  (setq japanese-TeX-engine-default 'uptex
        japanese-LaTeX-default-style "jlreq" ;; jsarticle? bxjsarticle?
        ;; japanese-LaTeX-command-default "LatexMk"
        )
  ;; latex.el
  (setq LaTeX-figure-label "fig:"
        LaTeX-table-label "tab:"
        LaTeX-section-label "sec:")
  ;; font-latex
  (setq font-latex-fontify-script nil
        font-latex-script-display nil
        font-latex-fontify-sectioning  1.0
        )
  )

Org-mode

org-mode が無いと生きていけない体になりました

基本設定

目新しい設定はしていない, と思う.
(leaf org
  :ensure t
  ;; 昔のメモ(howm)も org-mode で開く
  :mode "\\.org\\'" "\\.howm\\'"
  :bind (("C-x n s" . org-narrow-to-subtree)
         ("C-x n w" . widen)
         ("C-c a"   . org-agenda)
         ("C-x m"   . org-capture)
         )
  :preface
;;;###autoload
(defun my:org-timestamp-hook ()
  "`timestamp-start Change'の変更. org-mode では #+date: をひっかける"
  (set (make-local-variable 'time-stamp-start) "#\\+date: ")
  (set (make-local-variable 'time-stamp-end)   "\$")
  )
  ;; GTD: TODO→...→DONE としたエントリを =Arhive.org= に移動
  ;; - 一旦保留
  ;; (defun my:org-archive-done-tasks ()
  ;;   (interactive)
  ;;   (org-map-entries 'org-archive-subtree "/DONE" 'file))
  :hook
  `((org-mode-hook . (lambda ()
                       (my:org-timestamp-hook)
                       (org-tempo-setup)))
    (org-tab-before-tab-emulation-hook . org-tempo-complete-tag)
    ;; (org-todo-statistics-hook       . my:org-archive-done-tasks)
    ;; (org-todo-after-statistics-hook . my:org-archive-done-tasks)
    )
  :init
  (leaf org-tempo :commands (org-tempo-setup org-tempo-complete-tag))
  :config
  ;; Nextcloud に保存する
  (setq org-directory (expand-file-name my:d:org)
        ;; return でリンクを辿る
        org-return-follows-link    t
        ;; 見出しを畳んで表示
        org-startup-folded         t
        ;; 折り返し無し
        org-startup-truncated      t
        ;; インデントする
        org-adapt-indentation      t
        ;; link handler → xdg-open 任せ
        org-file-apps-defaults     '((remote . emacs)
                                     (system . "xdg-open %s")
                                     (t      . "xdg-open %s"))
        org-file-apps-defaults-gnu '((remote . emacs)
                                     (system . "xdg-open %s")
                                     (t      . "xdg-open %s"))
        org-file-apps              '((auto-mode . emacs)
                                     ("\\.mm\\'" . default)
                                     ("\\.x?html?\\'" . "xdg-open %s")
                                     ("\\.pdf\\'" . "xdg-open %s"))
        ;; GTD: 状態の追加
        org-todo-keywords          '((sequence "TODO(t)" "WAIT(w)" "SOMEDAY(s)" "|" "DONE(d)" "CANCEL(c)")
                                     (sequence "新規" "進行中" "解決" "フィードバック" "貸し出し可能" "貸し出し中" "|" "終了" "却下" "廃棄")
                                     (type "ARTICLE(a)" "|" "DONE(d)")
                                     (type "MEMO(m)" "|" "DONE(d)"))
        org-todo-keyword-faces     '(("TODO"    . org-todo)
                                     ("WAIT"    . org-todo)
                                     ("ARTICLE" . (:foreground "#7fbfff" :weight bold))
                                     ("MEMO"    . (:foreground "#7fbfff" :weight bold))
                                     ("SOMEDAY" . (:foreground "#7fff7f" :weight bold)))
        ;; GTD: タグの追加
        org-tag-alist              '(("OFFICE"     . ?o)
                                     ("HOME"       . ?h)
                                     ("MAIL"       . ?m)
                                     ("WORK"       . ?w)
                                     ("Debian"     . ?d)
                                     ("Computer"   . ?c)
                                     ("Book"       . ?b)
                                     ("Emacs"      . ?e)
                                     ("TeX"        . ?t)
                                     ("Ruby"       . ?r)
                                     ("IGNORE"     . ?i)
                                     ("PLANNED"    . ?p)
                                     )
        ;; Archive.org の位置指定
        org-archive-location       (expand-file-name "Archive.org::" my:d:org)
        ;; modules → とりあえずクリアしておく
        org-modules  '()
        ;; element-cache は使わない...うまく使えると良いんだけれど.
        org-element-use-cache nil
        ;; org-agenda で設定するべきかなぁ...
        org-agenda-inhibit-startup  t
        ;;
        org-emphasis-alist
        (cons '("+" '(:strike-through t :foreground "#999999"))
              (cl-delete "+" org-emphasis-alist :key 'car :test 'equal))
        )
  )

Org-Babel

(leaf org-src
  :config
  (setq org-src-fontify-natively         t ;; font-lock
        ;; TAB の挙動
        org-src-tab-acts-natively        t
        ;; インデント
        org-edit-src-content-indentation 0
        ;; インデントを残す
        org-src-preserve-indentation     t
        ;; load languages
        )
  )
(leaf org-babel
  :config
  (setq org-babel-load-languages '((emacs-lisp . t)))
  (if (file-exists-p "/usr/share/ditaa/ditaa.jar")
      (add-to-list 'org-babel-load-languages '(ditaa . t)))
  (if (executable-find "uplatex")
      (add-to-list 'org-babel-load-languages '(latex . t)))
  )
(leaf ob-ditaa
  :init
  (setq org-ditaa-jar-path "/usr/share/ditaa/ditaa.jar")
  )

Org-Id

(leaf org-id
  :bind
  (("C-c n o" . org-id-get-create))
  :init
  (leaf org-macs :commands org-with-point-at)
;;;###autoload
(defun my:org-id-add-custom-id ()
  "Add \"CUSTOM_ID\" to the current tree if not assigned yet."
  (interactive)
  (my:org-custom-id-get nil t))
;;;###autoload
(defun my:org-id-get-custom-id ()
  "Return a part of UUID with an \"org\" prefix.
g. \"org3ca6ef0c\"."
  (let* ((id (org-id-new "")))
    (when (org-uuidgen-p id)
      (downcase (concat "org"  (substring (org-id-new "") 0 8))))))
;;;###autoload
(defun my:org-custom-id-get (&optional pom create)
  "Get the CUSTOM_ID property of the entry at point-or-marker POM.
e https://writequit.org/articles/emacs-org-mode-generate-ids.html"
  (interactive)
  (eval-when-compile (require 'org-macs))
  (org-with-point-at pom
    (let ((id (org-entry-get nil "CUSTOM_ID")))
      (cond
       ((and id (stringp id) (string-match "\\S-" id))
        id)
       (create
        (setq id (my:org-id-get-custom-id))
        (unless id
          (error "Invalid ID"))
        (org-entry-put pom "CUSTOM_ID" id)
        (message "--- CUSTOM_ID assigned: %s" id)
        (org-id-add-location id (buffer-file-name (buffer-base-buffer)))
        id)))))
;;;###autoload
(defun my:org-id-delete-all-id-in-file ()
  (interactive)
  (goto-char 1)
  (while (not (eq (point) (point-max)))
    (org-next-visible-heading 1)
    (let ((id (org-entry-get (point) "ID")))
      (when id
        (message "ID: %s" id)
        (org-delete-property "ID"))))
  (message "--- done."))
;;;###autoload
(defun my:org-id-add-to-headlines-in-file ()
  "Add CUSTOM_ID properties to all headlines in the current file.
e https://writequit.org/articles/emacs-org-mode-generate-ids.html"
  (interactive)
  (save-excursion
    (widen)
    (goto-char (point-min))
    (when (re-search-forward "^#\\+options:.*auto-id:t" (point-max) t)
      (org-map-entries
       (lambda () (my:org-custom-id-get (point) 'create))))))
  ;;
  :hook (before-save-hook . (lambda ()
                              (when (and (eq major-mode 'org-mode)
                                         (eq buffer-read-only nil))
                                (my:org-id-add-to-headlines-in-file))))
  :config
  (setq org-id-locations-file (expand-file-name "org-id-locations" my:d:tmp)
        org-id-link-to-org-use-id 'create-if-interactive-and-no-custom-id)
  )

Org-journal: 日記

orgで日記. org-roam のメモにする前のエントリもどんどん書き込んでいたり.
(leaf org-journal
  :if (file-directory-p my:d:org)
  :commands org-journal-new-entry
  :ensure t
  :hook
  ((org-journal-mode-hook . (lambda()
                              (setq-local truncate-lines t))))
  :init
  (setq org-journal-file-type                 'yearly
        org-journal-dir                       (expand-file-name "journal" my:d:org)
        org-journal-file-format               "%Y.org"
        org-journal-cache-file                (expand-file-name "org-journal.cache" my:d:tmp)
        org-journal-date-format               "%x (%a)"
        org-journal-time-format               "<%Y-%m-%d %R> "
        org-journal-time-prefix               "** MEMO "
        org-journal-enable-agenda-integration t
        org-journal-find-file                 'find-file
        ;; org-journal-carryover-items           "TODO=\"TODO\""
        org-journal-carryover-delete-empty-journal 'always
        org-journal-start-on-weekday          0
        )
  :config
  (global-set-key (kbd "C-c C-j") 'browse-url-at-point) ;; 🤔?
  )

Org-roam: リンク付きメモ [0/2]

まだ今一使い熟せていない
  • [ ] メモ取りの時にイチイチタイトル打つの面倒じゃね?
  • [ ] メモが分散すると Agenda View で困りそうなんだけれど

どうするのが良いかなぁ.

現状は org-gcal 用の template を追加してある(org-captureを使うのを止めた).

(leaf org-roam
  :if (and (file-directory-p my:d:org)
           (executable-find "rg")
           (executable-find "sqlite3"))
  :ensure t
  :commands (org-roam-db-query)
  :init
  (setq org-roam-v2-ack t
        org-roam-db-location (expand-file-name "org-roam.db" my:d:tmp)
        org-roam-directory   (expand-file-name "roam" my:d:org))
  ;;
  :bind
  (("C-c n a" . org-roam-alias-add)
   ("C-c n f" . org-roam-node-find)
   ("C-c n i" . org-roam-node-insert)
   ("C-c n t" . org-roam-tag-add)
   ("C-c n l" . org-roam-buffer-toggle)
   )
  :defer-config
  (org-roam-db-autosync-enable)
  )

Org-capture: メモ取り

キーバインドは以前 changelog memo をやっていた時の癖で C-x m をメモにしている.
(leaf org-capture
  :if (file-directory-p my:d:org)
  :commands org-capture
  :defvar (my:org:calendar1 my:org:calendar2 my:org:calendar3 my:org:calendar4)
  :pl-setq (my:org:calendar1 my:org:calendar2 my:org:calendar3 my:org:calendar4)
  :config
  (defun my:org-journal-add-date-entry-capture ()
    (org-journal-new-entry t)
    (goto-char (point-max))
    )
  (setq org-default-notes-file (expand-file-name "INBOX.org" my:d:org))
  (setq org-capture-templates
        `(
          ("t" "TODO" plain
           (function my:org-journal-add-date-entry-capture)
           "** TODO %(format-time-string org-journal-time-format)%^{title} %^g\n  %?\n  %a"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("m" "メモを追加" plain
           (function my:org-journal-add-date-entry-capture)
           "** MEMO %(format-time-string org-journal-time-format)%?"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("a" "少し長めの記事を追加する" plain
           (function my:org-journal-add-date-entry-capture)
           "** ARTICLE %(format-time-string org-journal-time-format)%? "
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("s" "個人予定表スケジュールを追加" plain
           (file ,(expand-file-name "Schedule.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar1)\n  :org-gcal-managed: org\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("u" "仕事予定表スケジュールを追加" plain
           (file ,(expand-file-name "Univ.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar2)\n  :org-gcal-managed: org\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("g" "GFD 関連 スケジュールを追加" plain
           (file ,(expand-file-name "GFD.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar3)\n  :org-gcal-managed: org\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          ("h" "有給休暇・特別休暇を追加" plain
           (file ,(expand-file-name "Holidays.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar4)\n  :org-gcal-managed: org\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           ;; :unnarrowed nil
           :kill-buffer t
           )
          )
        )
  )

Org-agenda: スケジュール, TODO 表示

GTD 用の設定.後述の org-gcalorgmine で取得したデータも表示している. ついでに
  • 土曜日をの face を追加.
  • 祝日, 休日を日曜と同じfaceにする.

なんて事もやっている.元ネタは Org-mode and holidays.

(leaf org-agenda
  :if (file-directory-p my:d:org)
  :preface
  ;; face は theme にまわすべき, かな?
  (defface my:org-agenda-date-saturday '((t (:foreground "#7FBFFF" :bold t )))
    "Agenda 表示中の土曜日用のface"
    :group 'org-agenda )
  ;; こっからは org-gcal で同期したカレンダーの色
  (defface my:org-agenda-calendar-Univ '((t (:foreground "#7FFF7F")))
    "Agenda 表示中, Univ.org の表示 face"
    :group 'org-agenda )
  (defface my:org-agenda-calendar-Schedule '((t (:foreground "#7FFFFF")))
    "Agenda 表示中, Schedule.org の表示 face"
    :group 'org-agenda )
  (defface my:org-agenda-calendar-GFD '((t (:foreground "#FFFF7F")))
    "Agenda 表示中, GFD.org の表示 face"
    :group 'org-agenda )
  (defface my:org-agenda-calendar-DebianJP '((t (:foreground "#BF7FFF")))
    "Agenda 表示中, DebianJP.org の表示 face"
    :group 'org-agenda )
  ;; (defface my:org-agenda-calendar-SNS '((t (:foreground "#CCCCCC")))
  ;;   "Agenda 表示中, Mastodon log の表示 face"
  ;;   :group 'org-agenda )

  ;; font-lock の適用. loop減らせないかなぁ….
;;;###autoload
  (defun my:org-agenda-finalize-font-lock ()
    "Custom: apply custom font-lock"
    (save-excursion
      (goto-char (point-min))
      (while (re-search-forward "Univ:" nil t)
        (add-text-properties (match-beginning 0) (line-end-position)
                             '(face my:org-agenda-calendar-Univ)))
      (goto-char (point-min))
      (while (re-search-forward "Schedule:" nil t)
        (add-text-properties (match-beginning 0) (line-end-position)
                             '(face my:org-agenda-calendar-Schedule)))
      (goto-char (point-min))
      (while (re-search-forward "DebianJP:" nil t)
        (add-text-properties (match-beginning 0) (line-end-position)
                             '(face my:org-agenda-calendar-DebianJP)))
      (goto-char (point-min))
      (while (re-search-forward "GFD:" nil t)
        (add-text-properties (match-beginning 0) (line-end-position)
                             '(face my:org-agenda-calendar-GFD)))
      ;;    (goto-char (point-min))
      ;;    (while (re-search-forward "twitter:" nil t)
      ;;      (add-text-properties (match-beginning 0) (line-end-position)
      ;;                           '(face my:org-agenda-calendar-twitter)))
      (goto-char (point-min))
      (while (re-search-forward "祝日\\|休日\\|Holidays\\|誕生日" nil t)
        (add-text-properties (match-beginning 0) (line-end-position)
                             '(face org-agenda-date-weekend)))
      ))
  :hook (org-agenda-finalize-hook . (lambda ()
                                      (my:org-agenda-finalize-font-lock)
                                      ;; カーソルを先頭行に
                                      (goto-char (point-min)))
                                  )
  :init
  (leaf org-super-agenda
    :ensure t
    :config
    :hook (org-agenda-mode-hook . org-super-agenda-mode)
    )
  ;;
  :config
  (setq org-agenda-day-face-function
        ;; ---
        ;; 「org-modeのアジェンダで土曜日と日本の祝日を色づけする」
        ;; https://misohena.jp/blog/2021-08-29-colorize-saturday-and-japanese-holidays-in-org-agenda.html
        ;; ちょっと順番を変えて(「祝日, 日曜」を赤にしたいので)先に「日曜か日本の祝日」をチェックしている
        ;; ---
        (lambda (date)
          (let ((face
                 (cond
                  ;; 日曜日か日本の祝日
                  ((or (= (calendar-day-of-week date) 0)
                       (let ((calendar-holidays japanese-holidays))
                         (calendar-check-holidays date)))
                   'org-agenda-date-weekend)
                  ;; 土曜日
                  ((= (calendar-day-of-week date) 6)
                   'my:org-agenda-date-saturday)
                  ;; 普通の日
                  (t 'org-agenda-date))))
            ;; 今日は色を反転
            (if (org-agenda-today-p date) (list :inherit face :underline t) face)))
        ;;
        org-agenda-span 'week
        org-agenda-start-on-weekday nil ;; today!
        org-agenda-format-date "%Y/%m/%d (%a)"
        org-agenda-weekend-days '(0)    ;; sunday.
        org-agenda-ignore-properties '(effort appt)
        ;; org-agenda-repeating-timestampo-show-all t ;; 🤔
        org-agenda-sorting-strategy '((agenda habit-down time-up timestamp-down priority-down category-keep)
                                      (todo category-keep timestamp-down priority-down)
                                      (tags category-keep priority-down)
                                      (search category-keep))
        org-agenda-custom-commands  '(("n" "agenda and all TODO list"
                                       ((agenda ""
                                                ((org-agenda-ndays 1)
                                                 (org-agenda-entry-types '(:timestamp :sexp))))
                                       (todo "TODO")
                                       (todo "新規|着手|進行中|確認"
                                             ((org-super-agenda-groups '((:auto-property "om_project"))))
                                             )
                                       (todo "WAIT|SOMEDAY")
                                       ;; (todo "MEMO|ARTICLE")
                                        )
                                       nil ;;
                                       )
                                      ("w" todo "TODO")
                                      )
        )
  :defer-config
  (setq org-agenda-files (list (concat my:d:org "/journal")))
  (dolist (file
           '(;; Archive.org   ← 🤔
             ;;; Calendar
             "DebianJP.org"
             "GFD.org"
             "Holidays.org"
             "Schedule.org"
             "Univ.org"
             ;;; Project
             "redmine_GFD.org"
             "redmine_SSKLAB.org"
             ;; "redmine_FluidSoc.org"
             ;;; misc
             "Anniversary.org"
             ;; "twitter.org"
             ))
    (add-to-list 'org-agenda-files (expand-file-name file my:d:org)))
  )

OrgとGoogle カレンダーの連携: org-gcal

request token 等の置き場所の変更 実際の情報等は password-store を使って設定しておく. ついでに agenda 表示の際の色付けを設定.
(leaf org-gcal
  :if (and my:d:password-store
           (and (file-directory-p my:d:org)
                (executable-find "curl")))
  :pl-setq
  (org-gcal-client-id
   org-gcal-client-secret
   org-gcal-file-alist)
  :ensure t
  :commands (org-gcal-fetch
             org-gcal-sync
             org-gcal-post-at-point
             org-gcal-reload-client-id-secret)
  :preface
  (setq org-gcal-dir (expand-file-name "org-gcal" my:d:tmp))
  (unless (file-directory-p org-gcal-dir)
    (make-directory org-gcal-dir))
  :init
  (leaf oauth2-auto
    ;; 🤔
    :config
    (setq oauth2-auto-plstore (expand-file-name "/run/user/1000/psd/uwabami-emacs-cache/oauth2-auto.plist"))
    )
  (leaf org-generic-id
    :init
    (setq org-generic-id-locations-file (expand-file-name "org-generic-id-locations"  my:d:tmp))
    )
  (leaf request-deferred :ensure t)
  (leaf request
    :ensure t
    :preface
    (setq request-storage-directory (expand-file-name "request" my:d:tmp))
    (unless (file-directory-p request-storage-directory)
      (make-directory request-storage-directory))
    ;; :config
    ;; (setq request-storage-directory (expand-file-name "request" my:d:tmp))
    )
  (leaf persist
    :ensure t
    :preface
    (setq persist--directory-location (expand-file-name "persist" my:d:tmp))
    (unless (file-directory-p persist--directory-location)
      (make-directory persist--directory-location))
    )
  ;;
  (setq org-gcal-dir (expand-file-name "org-gcal" my:d:tmp)
        org-gcal-token-file (expand-file-name "org-gcal/org-gcal-token" my:d:tmp)
        org-gcal-down-days 180  ;; 未来 180 日
        org-gcal-up-days 30  ;; 過去 30 日
        org-gcal-auto-archive t
        org-gcal-notify-p nil
        org-gcal-remove-api-cancelled-evetnts t
        org-gcal-remove-events-with-cancelled-todo t
        org-gcal-remove-api-cancelled-events  t
        alert-log-messages t
        alert-default-style 'libnotify
        )
  )

OrgとRedmine の連携: orgmine

素晴しい!! kametoku/orgmine: Emacs minor mode for org-mode with redmine integration
(leaf *orgmine
  :if  (and my:d:password-store
            (file-directory-p my:d:org))
  :hook
  `(org-mode-hook
    . (lambda ()
        (if (assoc "om_server" org-keyword-properties)
            (orgmine-mode))))
  :init
  (setq enable-local-variables :safe)
  (leaf elmine :ensure t)
  ;; (add-hook 'org-mode-hook
  ;;           (lambda ()
  ;;             (if (assoc "om_server" org-file-properties) (orgmine-mode))))
  (leaf orgmine
    :vc (:url "https://github.com/kametoku/orgmine" )
    :commands (orgmine-mode)
    :init
    ;; (defun my:orgmine-default-todo-keyword ()
    ;;   "Custom: use `org-file-properties' for backward compatibility."
    ;;   (or (cdr (assoc-string "om_default_todo" org-file-properties))
    ;;       orgmine-default-todo-keyword
    ;;       (nth 0 org-todo-keywords-1)
    ;;       1))

    ;; (defun my:orgmine-setup ()
    ;;   "Custom: use `org-file-properties' for backward compatibility."
    ;;   (let* ((server (cdr (assoc-string "om_server" org-file-properties t)))
    ;;          (config (cdr (assoc-string server orgmine-servers t))))
    ;;     (if config
    ;;         (set (make-local-variable 'orgmine-server) server))
    ;;     (mapc (lambda (elem)
    ;;             (let* ((key (car elem))
    ;;                 (symbol (intern (format "orgmine-%s" key)))
    ;;                 (value (cdr elem)))
    ;;               (if (memq key orgmine-valid-variables)
    ;;                (progn
    ;;                  (set (make-local-variable symbol) value)
    ;;                  (if (eq key 'custom-fields)
    ;;                      (orgmine-setup-custom-fields value)))
    ;;              (message "orgmine-setup: %s: skipped - invalid name" key))))
    ;;           config))
    ;;   (orgmine-setup-tags)
    ;;   (run-hooks 'orgmine-setup-hook))
    ;; :advice
    ;; '((:override orgmine-default-todo-keyword my:orgmine-default-todo-keyword)
    ;;   (:override orgmine-setup my:orgmine-setup))
    :pl-setq orgmine-servers
    :config
    (setq orgmine-note-block-begin "#+begin_src gfm"   ;; 要調整
          orgmine-note-block-end   "#+end_src\n")
    )
  )

Org-Wiki

(leaf org-wiki
  :if (file-directory-p "~/Public/cc-env")
  :vc (:url "https://github.com/uwabami/org-wiki")
  :init
  (setq org-wiki-location-alist     '("~/Public/cc-env")
        org-wiki-location           "~/Public/cc-env"
        org-wiki-publish-relative   t
        org-wiki-publish-root       "{{site.url}}/cc-env"
        org-wiki-completing-backend 'completing-read
        org-wiki-template (concat "#+TITLE: %n\n"
                                  "#+date: 20\n"
                                  "#+LAYOUT: default\n"
                                  "#+PREMALINK: /cc-env/%n.html\n"
                                  "#+options: auto-id:nil\n"
                                  "#+REF: cc-env/%n\n"
                                  "Related [[wiki:index][Index]] [[~/Public/cc-env/index.org::#orge0707863][Debian]]\n"
                                  "* %n\n"
                                  )
        )
  )

Org-Export

全般設定

latex, beamer,jekyll(後述) のみを有効に.
(leaf ox
  :preface
  ;; 空行の削除
  (defun my:remove-org-newlines-at-cjk-text (&optional _mode)
    "先頭が '*', '#', '|' でなく、改行の前後が日本の文字の場合は改行を除去"
    (interactive)
    (goto-char (point-min))
    (while (re-search-forward "^\\([^|#*\n].+\\)\\(.\\)\n *\\(.\\)" nil t)
      ;; < <
      (if (and (> (string-to-char (match-string 2)) #x2000)
               (> (string-to-char (match-string 3)) #x2000))
          (replace-match "\\1\\2\\3"))
      (goto-char (line-beginning-position))))
  :init
  (setq org-export-backends             '(;; remove somve built-in
                                          ;; html
                                          jekyll
                                          latex
                                          beamer)
        org-export-with-toc             nil
        org-export-with-section-numbers nil
        )
  )

Jekyll, HTML

Web サイトは Jekyll で作成しています. 以前は org file を直接 jekyll で処理していましたが, 最近は org を html に export して, それを処理する様にしています.
  • [ ] finalized の呼び出しがダサいので整理すること!

exporter は uwabami/ox-jekyll にあります.

(leaf ox-html
  :after ox
  :init
  (leaf ox-jekyll
    :if (file-directory-p "~/Public/cc-env")
    :vc (:url "https://github.com/uwabami/ox-jekyll")
    )
  (leaf s :ensure t)
  ;;
  (setq org-html-table-align-individual-fields nil
        org-html-table-default-attributes      nil
        org-html-html5-fancy                   t
        org-html-doctype                       "html5"
        org-html-inline-image-rules           '(("file"  . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|webp\\|svg\\)\\'")
                                                ("http"  . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|webp\\|svg\\)\\'")
                                                ("https" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|webp\\|svg\\)\\'"))
        )
  :config
  ;;
  (defun my:org-wiki-jekyll-finalized1 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (require 's)
      (s-replace
       (format "<a href=\"file://%sREADME.html"
               (expand-file-name user-emacs-directory))
       "<a href=\"{{baseurl}}/cc-env/Emacs.html"
       contents)))
  ;;
  (defun my:org-wiki-jekyll-finalized2 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (require 's)
      (s-replace
       (format "<a href=\"file://%s"
               (expand-file-name "Public/" (getenv "HOME")))
       "<a href=\"{{site.url}}/"
       contents)))
  ;;
  (defun my:org-wiki-jekyll-finalized3 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (replace-regexp-in-string
       " id=\"outline-container-org.+\" class=\"outline-.+\""
       "" contents)))
  ;;
  (defun my:org-wiki-jekyll-finalized4 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (replace-regexp-in-string
       "<a id=\"org.+?\"></a>"
       "" contents)))
  ;;
  (defun my:org-wiki-jekyll-finalized5 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (replace-regexp-in-string
       "<pre class=\"example\" id=\".+?\">"
       "<pre class=\"example\">" contents)))
  ;;
  (defun my:org-wiki-jekyll-finalized6 (contents backend info)
    "Replace some URL"
    (ignore info)
    (when (org-export-derived-backend-p backend 'jekyll)
      (replace-regexp-in-string
       "<figure id=\".+?\">"
       "<figure>" contents)))
  ;; 整理しないと...
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized1)
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized2)
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized3)
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized4)
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized5)
  (add-to-list 'org-export-filter-body-functions 'my:org-wiki-jekyll-finalized6)
  )

LaTeX, Beamer

LaTeX 関連
(leaf ox-latex
  :after ox
  :init
  (leaf oc-natbib :after ox :require t)
  ;;
  (setq org-latex-default-class "my:uplatex"
        org-latex-pdf-process   '("latexmk -r ~/.latexmkrc -pdfdvi -recorder -output-directory='%o' %f")
        org-latex-compiler      "latexmk"
        org-latex-listings      'minted
        org-latex-minted-options '(("frame" "lines")
                                   ("framesep=2mm")
                                   ("linenos=true")
                                   ("baselinestretch=1.2")
                                   ("breaklines"))
        org-latex-default-packages-alist  '(("utf8" "inputenc" t nil)
                                            ;; ("prefernoncjk" "pxcjkcat" t nil)
                                            ("T1" "fontenc" t nil)
                                            ("" "graphicx" t nil)
                                            ("normalem" "ulem" t nil) ;; udline に変更すべきかな?
                                            ("" "amsmath" t nil)
                                            ("" "amssymb" t nil)
                                            ("" "physics" t nil)
                                            ;; ("" "siunitx" t nil)
                                            ("" "hyperref" t nil)
                                            ("" "multirow" t nil)
                                            ("" "ltablex" t nil)
                                            ("" "wrapfig" t nil)
                                            ("" "silence" t nil)
                                            ("" "arydshln" t nil)
                                            )
        )
  ;;
  (setq org-format-latex-header "\\documentclass[dvipdfmx]{standalone}
\\usepackage[usenames]{color}
\\usepackage{pgfplots}
\\usepgfplotslibrary{fillbetween,patchplots}
\\pgfplotsset{compat=newest}
\\usepackage{tikz}
\\usetikzlibrary{intersections,calc,arrows,arrows.meta,decorations.markings,fadings,decorations.pathreplacing,patterns}
\\definecolor{cud_blue}{HTML}{005aff}
\\definecolor{cud_green}{HTML}{03af7a}
\\definecolor{cud_orange}{HTML}{f6aa00}
\\definecolor{cud_red}{HTML}{ff4b00}
\\definecolor{cud_yellow}{HTML}{fff100}
\\definecolor{cud_cyan}{HTML}{4dc4ff}
\\definecolor{cud_pink}{HTML}{ff8082}
\\definecolor{cud_purple}{HTML}{990099}
\\definecolor{cud_brown}{HTML}{804000}
\\definecolor{cud_lightgrey}{HTML}{c8c8cb}
\\definecolor{cud_grey}{HTML}{84919e}
\\definecolor{blackblue}{HTML}{2C3E50}
\\def\\pgfsysdriver{pgfsys-dvipdfmx.def}
\[PACKAGES]
\[DEFAULT-PACKAGES]")
  (setq org-latex-hyperref-template "\\hypersetup{
pdfauthor={%a},
pdftitle={%t},
pdfkeywords={%k},
pdfsubject={%s},
pdfcreator={%c},
pdflang={%l}}
")
  (setq org-latex-classes '(("my:uplatex"
                             "\\RequirePackage{plautopatch}\n\\documentclass[a4paper,uplatex,dvipdfmx]{jsarticle}\n\\plautopatchdisable{eso-pic}\n\\ifdefined\\endofdump\\else\\let\\endofdump\\relax\\fi
            [DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
                             ("\\section\{%s\}" . "\\section*\{%s\}")
                             ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
                             ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))
                            ("my:beamer"
                             "\\RequirePackage{plautopatch}\n\\documentclass[dvipdfmx,c,presentation]{beamer}\n\\plautopatchdisable{eso-pic}\n\\ifdefined\\endofdump\\else\\let\\endofdump\\relax\\fi
            [DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
                             ("\\section\{%s\}" . "\\section*\{%s\}")
                             ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
                             ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))
                            ("my:beamer169"
                             "\\RequirePackage{plautopatch}\n\\documentclass[dvipdfmx,c,presentation,aspectratio=169]{beamer}\n\\plautopatchdisable{eso-pic}\n\\ifdefined\\endofdump\\else\\let\\endofdump\\relax\\fi
            [DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
                             ("\\section\{%s\}" . "\\section*\{%s\}")
                             ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
                             ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))
                            )
        )
  )

Beamer 関連: SHORT 系を追加している

(leaf ox-beamer
  :after ox-latex
  :init
  (setq org-beamer-frame-level 2
        org-beamer-frame-default-options "fragile"
    )
  ;;
  (add-to-list 'org-export-options-alist
               '(:shortdate      "SHORT_DATE"      nil nil))
  (add-to-list 'org-export-options-alist
               '(:shorttitle     "SHORT_TITLE"     nil nil))
  (add-to-list 'org-export-options-alist
               '(:shortauthor    "SHORT_AUTHOR"    nil nil))
  (add-to-list 'org-export-options-alist
               '(:institute      "INSTITUTE"       nil nil))
  (add-to-list 'org-export-options-alist
               '(:shortinstitute "SHORT_INSTITUTE" nil nil))
  (add-to-list 'org-export-options-alist
               '(:outlinetitle   "OUTLINE_TITLE" nil nil))
  ;; customize
  (defun my:org-beamer-template (contents info)
    "Custom: support shortdate, shorttile, shortauthor, institute for beamer export"
    (let ((title (org-export-data (plist-get info :title) info))
          (subtitle (org-export-data (plist-get info :subtitle) info)))
      (concat
       ;; Time-stamp.
       (and (plist-get info :time-stamp-file)
            (format-time-string "%% Created %Y-%m-%d %a %H:%M\n"))
       ;; LaTeX compiler
       (org-latex--insert-compiler info)
       ;; Document class and packages.
       (org-latex-make-preamble info)
       ;; Insert themes.
       (let ((format-theme
              (lambda (prop command)
                (let ((theme (plist-get info prop)))
                  (when theme
                    (concat command
                            (if (not (string-match "\\[.*\\]" theme))
                                (format "{%s}\n" theme)
                              (format "%s{%s}\n"
                                      (match-string 0 theme)
                                      (org-trim
                                       (replace-match "" nil nil theme))))))))))
         (mapconcat (lambda (args) (apply format-theme args))
                    '((:beamer-theme "\\usetheme")
                      (:beamer-color-theme "\\usecolortheme")
                      (:beamer-font-theme "\\usefonttheme")
                      (:beamer-inner-theme "\\useinnertheme")
                      (:beamer-outer-theme "\\useoutertheme"))
                    ""))
       ;; Possibly limit depth for headline numbering.
       (let ((sec-num (plist-get info :section-numbers)))
         (when (integerp sec-num)
           (format "\\setcounter{secnumdepth}{%d}\n" sec-num)))
       ;; custom: Author support `shortauthor'
       (let ((author (and (plist-get info :with-author)
                          (let ((auth (plist-get info :author)))
                            (and auth (org-export-data auth info)))))
             (shortauthor (plist-get info :shortauthor))
             (email (and (plist-get info :with-email)
                         (org-export-data (plist-get info :email) info))))
         (cond ((and author shortauthor) (format "\\author[%s]{%s}\n" shortauthor author))
               ((and author shortauthor email (not (string= "" email)))
                (format "\\author[%s]{%s\\thanks{%s}}\n" shortauthor author email))
               ((and author email (not (string= "" email)))
                (format "\\author{%s\\thanks{%s}}\n" author email))
               ((or author email) (format "\\author{%s}\n" (or author email)))))
       ;; custom: Date support `shortdate'
       (let ((date (and (plist-get info :with-date) (org-export-get-date info)))
             (shortdate (plist-get info :shortdate)))
         (cond ((and date shortdate)
                (format "\\date[%s]{%s}\n" shortdate (org-export-data date info)))
               (t (format "\\date{%s}\n" (org-export-data date info)))))
       ;; custom: Title support `shorttitle'
       (let ((shorttitle (plist-get info :shorttitle)))
         (cond ((and title shorttitle) (format "\\title[%s]{%s}\n" shorttitle title))
               (t (format "\\title{%s}\n" title))))
       ;; custom: support `institute', `shortinstitute'
       (let ((institute (plist-get info :institute))
             (shortinstitute (plist-get info :shortinstitute)))
         (cond ((and institute shortinstitute)
                (format "\\institute[%s]{%s}\n" shortinstitute institute))
               ((or institute shortinstitute)
                (format "\\institute{%s}\n" (or shortinstitute institute)))))
       (when (org-string-nw-p subtitle)
         (concat (format (plist-get info :beamer-subtitle-format) subtitle) "\n"))
       ;; Beamer-header
       (let ((beamer-header (plist-get info :beamer-header)))
         (when beamer-header
           (format "%s\n" (plist-get info :beamer-header))))
       ;; 9. Hyperref options.
       (let ((template (plist-get info :latex-hyperref-template)))
         (and (stringp template)
              (format-spec template (org-latex--format-spec info))))
       ;; Document start.
       "\\begin{document}\n\n"
       ;; Title command.
       (org-element-normalize-string
        (cond ((not (plist-get info :with-title)) nil)
              ((string= "" title) nil)
              ((not (stringp org-latex-title-command)) nil)
              ((string-match "\\(?:[^%]\\|^\\)%s"
                             org-latex-title-command)
               (format org-latex-title-command title))
              (t org-latex-title-command)))
       ;; Table of contents.
       (let ((depth (plist-get info :with-toc)))
         (when depth
           (concat
            (format "\\begin{frame}%s{%s}\n"
                    (org-beamer--normalize-argument
                     (plist-get info :beamer-outline-frame-options) 'option)
                    (cond ((plist-get info :outlinetitle)
                           (plist-get info :outlinetitle))
                          (t
                           (plist-get info :beamer-outline-frame-title)))
                    )
            (when (wholenump depth)
              (format "\\setcounter{tocdepth}{%d}\n" depth))
            "\\tableofcontents\n"
            "\\end{frame}\n\n")))
       ;; Document's body.
       contents
       ;; Creator.
       (if (plist-get info :with-creator)
           (concat (plist-get info :creator) "\n")
         "")
       ;; Document end.
       "\\end{document}")))
  ;;
  :advice ((:override org-beamer-template
                      my:org-beamer-template))
  )

Publish

(leaf ox-publish
  :if (file-directory-p "~/Public/cc-env")
  :after (ox-jekyll org-wiki)
  :commands my:org-wiki-publish
  :init
  (setq org-publish-timestamp-directory (expand-file-name "org-timestamps/" my:d:tmp))
  :config
  (defun my:org-wiki-publish ()
    (interactive)
    (org-publish (org-wiki-make-org-publish-plist
                  'org-jekyll-publish-to-html)
                 t))
  )

SOMEDAY 日記: tDiary [0/1]

  • [ ] org2blog で tDiary を更新できないか妄想している
(leaf tdiary-mode
  :if (and my:d:password-store
           (file-directory-p (concat (getenv "HOME") "/Nextcloud/tdiary")))
  :commands (tdiary-mode tdiary-replace tdiary-append tdiary-passwd-file-load)
  :defvar (tdiary-csrf-key tdiary-passwd-file tdiary-passd-file)
  :pl-setq (tdiary-csrf-key tdiary-passwd-file)
  :vc (:url "https://github.com/uwabami/tdiary-mode")
  :config
  (setq tdiary-text-directory (concat (getenv "HOME") "/Nextcloud/tdiary/")
        tdiary-diary-list '(("log" "https://uwabami.junkhub.org/log/"))
        tdiary-style-mode 'org-mode
        tdiary-text-suffix ".org"
        tdiary-http-timeout 100)
  (tdiary-passwd-file-load)
  )

VCSなど, ソースコード管理関係

まあ, ほとんど Git 関連な訳ですが.

project.el

Emacs 27.1 から built-in となった. eglot でも使われているので, 設定しておく.
(leaf project
  :ensure t
  :init
  (setq project-list-file (expand-file-name "projects" my:d:tmp)
        project-switch-use-entire-map t)
  )
  • [ ] ghq の list を追加できないかな? できれば --full-path じゃない表示にしたい(my:shorten-file-path を噛ませたい).

consult-ghq: ghq を consult で.

project.el で ghq を使うようになったら不要かもしれないけど, とりあえず.
(leaf consult-ghq
  :vc (:url "https://github.com/uwabami/consult-ghq")
  :bind (("C-x f" . consult-ghq-open))
  :init
  (setq consult-ghq-short-list t)
  )

magit:

magit は Emacs の Git Frontend. 結局の所 CUI でコマンド叩く事も多いけれど, これはこれで重宝している.
(leaf magit
  :bind (("C-x g" . magit-status))
  :ensure t
  :init
  (leaf transient
    :init
    (setq transient-history-file (expand-file-name "transient-history.el" my:d:tmp)
          transient-levels-file (expand-file-name "transient-levels.el" my:d:tmp)
          transient-values-file (expand-file-name "transient-values.el" my:d:tmp)
          transient-force-fixed-pitch t
          )
    )
  ;;
  (setq magit-completing-read-function 'magit-builtin-completing-read
        magit-refs-show-commit-count   'all
        magit-log-buffer-file-locked   t
        magit-revision-show-gravatars  nil
        )
  )

Ruby

(leaf ruby-mode :ensure t)

その他のモード設定

ほぼ読み込むだけの mode の設定. 設定が増えたら別途まとめる。
(leaf lua-mode :ensure t)
(leaf ssh-config-mode :ensure t)
(leaf fish-mode :ensure t :mode "\\.fish\\'")
(leaf rd-mode
  :mode "\\.rd\\'"
  :hook
  (rd-mode-hook . rd-show-other-block-all))
(leaf scss-mode
  :ensure t
  :init
  (leaf css-mode
    :init
    (setq css-indent-offset 2))
  :mode "\\.sass\\'" "\\.scss\\'")
(leaf generic-x)
(leaf systemd :ensure t)
;; (leaf *misc-mode
;;   :init
;;   (leaf systemd :ensure t)
(leaf debian-el
  :ensure t
  :init
  (setq debian-bug-download-directory "~/Downloads")
  )
(leaf yaml-mode :ensure t :mode "\\.yml\\'" "\\.yaml\\'")
;;   (leaf textile-mode :ensure t)
(leaf dpkg-dev-el :ensure t)
;;   (leaf sh-mode
;;     :custom ((system-uses-terminfo . nil))
;;     )
;;   (leaf apt-sources-list
;;     :custom
;;     ((apt-sources-list-suites
;;       . '("stable" "stable-backports"
;;           "testing" "testing-backports"
;;           "unstable" "experimental"
;;           "jessie" "jessie-backports"
;;           "stretch" "stretch-backports"
;;           "buster" "buster-backports"
;;           "bullseye" "bullseye-backports"
;;           "sid")))
;;     )
;;   (leaf info-colors
;;     :ensure t
;;     :hook
;;     (Info-selection #'info-colors-fontify-node))
;;   )

カラーコードに色付け: rainbow-mode

#RRGGBB のカラーコードに勝手に色が付く.CSS の編集中なんかで地味に便利.
(leaf rainbow-mode :ensure t :hook (emacs-startup-hook . rainbow-mode))

テーマとモードライン

theme: modus-theme

デフォルトの font-lock を(好みに合わせて)入れ替えたり.
(leaf modus-themes
  :ensure t
  :bind ("<f5>" . modus-themes-toggle)
  :commands modus-themes-load-theme
  :hook
  `((emacs-startup-hook . (lambda()
                            (modus-themes-load-theme 'modus-vivendi)))
    (modus-themes-after-load-theme-hook . my:modus-themes-custom-faces)
    )
  :init
  (setq modus-themes-custom-auto-reload t
        modus-themes-disable-other-themes t
        modus-themes-bold-constructs nil
        modus-themes-italic-constructs nil
        modus-themes-mixed-fonts nil
        modus-themes-prompts '(bold bold)
        modus-themes-completions '((matches   . (bold))
                                   (selection . (bold underline)))
        modus-themes-headings '((t . (bold)))
        modus-themes-variable-pitch-ui nil
        modus-vivendi-palette-overrides
        '(;;
          ;; Basic color override
          (green-cooler "#bfff7f")
          (green-intense "#4ceea5")
          (yellow "#efef00")
          (yellow-warmer "#ffb347")
          (yellow-intense "#ffff7f")
          ;;
          ;; (bg-yellow-intense "#556b2f")
          ;;
          (bg-main 'unspecified)
          (comment fg-dim)
          ;; (bg-hl-line "#556b2f")
          ;;
          ;; Code remapping
          ;;
          (builtin blue-warmer)
          (constant yellow-warmer)
          (docstring green-intense)
          (docmarkup green-intense)
          (fnname magenta-cooler)
          (keyword red-faint)
          (preprocessor red-cooler)
          (string cyan-cooler)
          (type yellow-intense)
          (variable blue)
          ;;
          (rx-construct blue-faint)
          (rx-backslash green-cooler)
          ;;
          (fg-link cyan-warmer)
          ;;
          (err red-intense)
          (warning magenta-intense)
          (info cyan-intense)
          ;;
          ;; Org Agenda view
          ;; (date-common fg-alt)
          ;; (date-deadline red-faint)
          ;; (date-event fg-alt)
          ;;
          (date-common fg-alt)
          (date-weekday fg-main)
          (date-weekend red-faint)
          ;; headline
          (fg-heading-0 cyan-intense)
          (fg-heading-1 magenta-cooler)
          (fg-heading-2 blue-cooler)
          (fg-heading-3 yellow-warmer)
          (fg-heading-4 green-faint)
          (fg-heading-5 yellow-intense)
          (fg-heading-6 red-cooler)
          (fg-heading-7 magenta-warmer)
          (fg-heading-8 fg-main)
          )
        )
  :config
  (defun my:modus-themes-custom-faces (&rest _)
    (modus-themes-with-colors
      (custom-set-faces
       ;; vertico
       `(vertico-current                                  ((,c :inherit hl-line :background unspecified )))
       ;; wanderlust
       `(wl-highlight-folder-closed-face                  ((,c :foreground ,green-cooler )))
       `(wl-highlight-folder-few-face                     ((,c :foreground ,red-cooler )))
       `(wl-highlight-folder-killed-face                  ((,c :foreground ,fg-alt )))
       `(wl-highlight-folder-many-face                    ((,c :foreground ,magenta )))
       `(wl-highlight-folder-opened-face                  ((,c :foreground ,cyan-cooler )))
       `(wl-highlight-folder-unknown-face                 ((,c :foreground ,cyan-warmer )))
       `(wl-highlight-folder-unread-face                  ((,c :foreground ,blue-warmer )))
       `(wl-highlight-folder-zero-face                    ((,c :foreground ,fg-main )))
       `(wl-highlight-message-citation-header             ((,c :foreground ,green-warmer )))
       `(wl-highlight-message-cited-text-1                ((,c :foreground ,green-intense )))
       `(wl-highlight-message-cited-text-2                ((,c :foreground ,yellow-intense )))
       `(wl-highlight-message-cited-text-3                ((,c :foreground ,blue-intense )))
       `(wl-highlight-message-cited-text-4                ((,c :foreground ,cyan-intense )))
       `(wl-highlight-message-cited-text-5                ((,c :foreground ,magenta-cooler )))
       `(wl-highlight-message-cited-text-6                ((,c :foreground ,red-intense )))
       `(wl-highlight-message-cited-text-7                ((,c :foreground ,green-intense )))
       `(wl-highlight-message-cited-text-8                ((,c :foreground ,yellow-intense )))
       `(wl-highlight-message-cited-text-9                ((,c :foreground ,blue-intense )))
       `(wl-highlight-message-cited-text-10               ((,c :foreground ,cyan-intense )))
       `(wl-highlight-message-header-contents             ((,c :foreground ,magenta-cooler )))
       `(wl-highlight-message-headers                     ((,c :foreground ,cyan-intense )))
       `(wl-highlight-message-important-header-contents2  ((,c :foreground ,magenta-intense )))
       `(wl-highlight-message-signature                   ((,c :foreground ,fg-dim )))
       `(wl-highlight-summary-answered-face               ((,c :foreground ,green-intense )))
       `(wl-highlight-summary-deleted-face                ((,c :foreground ,blue-intense )))
       `(wl-highlight-summary-disposed-face               ((,c :foreground ,fg-dim )))
       `(wl-highlight-summary-flagged-face                ((,c :foreground ,yellow-intense )))
       `(wl-highlight-summary-high-unread-face            ((,c :foreground ,red-intense )))
       `(wl-highlight-summary-low-unread-face             ((,c :foreground ,red-intense )))
       `(wl-highlight-summary-normal-face                 ((,c :foreground ,fg-main )))
       `(wl-highlight-summary-refiled-face                ((,c :foreground ,blue-warmer )))
       `(wl-highlight-summary-spam-face                   ((,c :foreground ,magenta-intense )))
       `(wl-highlight-summary-thread-top-face             ((,c :foreground ,fg-main )))
       `(wl-highlight-summary-unread-face                 ((,c :foreground ,fg-main )))
       `(wl-highlight-summary-new-face                    ((,c :foreground ,red-intense )))
       `(wl-highlight-summary-displaying-face             ((,c :underline t :bold t)))
       `(wl-highlight-folder-path-face                    ((,c :underline t :bold t)))
       ;; whitespace-mode
       `(whitespace-trailing                              ((,c :foreground ,magenta-intense )))
       `(whitespace-tab                                   ((,c :foreground ,blue-intense :underline t)))
       `(whitespace-space                                 ((,c :foreground ,green-intense :bold t)))
       `(whitespace-empty                                 ((,c :background ,red-intense )))
       )))
  )

modeline: powerline

(leaf powerline
  :ensure t
  :defvar
  (skk-indicator-alist
   skk-hiragana-mode-string
   skk-katakana-mode-string
   skk-latin-mode-string
   skk-jisx0208-latin-mode-string
   skk-jisx0201-mode-string
   skk-abbrev-mode-string
   skk-modeline-input-mode
   )
  :init
  ;; (defun my:major-mode-icon (mode)
  ;;   "Update file icon in mode-line, just display major-mode icon. not filename."
  ;;   (let* ((icon (all-the-icons-icon-for-mode mode)))
  ;;     (if (symbolp icon)
  ;;         (all-the-icons-faicon "file-code-o"
  ;;                               :face 'all-the-icons-dsilver
  ;;                               :height 1.0)
  ;;       icon)))
  ;;
  (defun my:skk-init-modeline-input-mode ()
    "Custom skkが読み込まれていなくても skk-modeline-input-mode に値を設定"
    (cond
     ((not (boundp 'skk-modeline-input-mode))
      (setq skk-modeline-input-mode "--SKK"))
     (t skk-modeline-input-mode)))
  ;;
  (defun my:skk-modeline-input-mode ()
    "Custom: powerline 用に skk の indicator を準備"
    (cond
     ((string-match "--SKK" skk-modeline-input-mode) "[--]")
     ((string-match skk-hiragana-mode-string skk-modeline-input-mode) "[あ]")
     ((string-match skk-katakana-mode-string skk-modeline-input-mode) "[ア]")
     ((string-match skk-latin-mode-string skk-modeline-input-mode)    "[_A]")
     ((string-match skk-jisx0208-latin-mode-string skk-modeline-input-mode) "[A]")
     ((string-match skk-jisx0201-mode-string skk-modeline-input-mode) "[_ア]")
     ((string-match skk-abbrev-mode-string skk-modeline-input-mode)   "[aA]")
     (t "[--]")
     )
    )
  ;;
  (defun my:skk-setup-modeline ()
    "skk-setup-modeline による modeline の更新を無効化"
    (setq skk-indicator-alist (skk-make-indicator-alist))
    (force-mode-line-update t))
  ;;
  :advice (:override skk-setup-modeline my:skk-setup-modeline)
  :init
  (setq powerline-buffer-size-suffix    nil
        powerline-display-hud           nil
        powerline-display-buffer-size   nil
        powerline-text-scale-factor      1
        powerline-default-separator     'utf-8
        powerline-utf-8-separator-left  #xe0b0
        powerline-utf-8-separator-right #xe0b2
        )
  :hook (emacs-startup-hook . my:powerline-theme)
  :config
;;;###autoload
  (defun my:powerline-theme ()
    "Setup the default mode-line."
    (interactive)
    (my:skk-init-modeline-input-mode)
    (setq-default
     mode-line-format
     '("%e"
       (:eval
        (let* ((active (powerline-selected-window-active))
               (mode-line-buffer-id (if active 'mode-line-buffer-id 'mode-line-buffer-id-inactive))
               (mode-line (if active 'mode-line 'mode-line-inactive))
               (face0 (if active 'powerline-active0 'powerline-inactive0))
               (face1 (if active 'powerline-active1 'powerline-inactive1))
               (face2 (if active 'powerline-active2 'powerline-inactive2))
               (separator-left (intern (format "powerline-%s-%s"
                                               (powerline-current-separator)
                                               (car powerline-default-separator-dir))))
               (separator-right (intern (format "powerline-%s-%s"
                                                (powerline-current-separator)
                                                (cdr powerline-default-separator-dir))))
               (lhs (list (powerline-raw (format "%s" (my:skk-modeline-input-mode)) mode-line 'l)
                          (powerline-raw "%*" mode-line 'l)
                          (powerline-raw mode-line-mule-info mode-line 'l)
                          ;; (powerline-raw (my:major-mode-icon major-mode) mode-line 'l)
                          (powerline-buffer-id mode-line-buffer-id 'l)
                          (powerline-raw " ")
                          ;; (funcall separator-left face0 face1)
                          ))
               (rhs (list (powerline-raw global-mode-string face1 'r)
                          ;; (funcall separator-right face2 face1)
                          (powerline-vc face1 'r)
                          (powerline-raw mode-line-misc-info 'r)
                          (powerline-raw " ")
                          (powerline-raw "%6p" mode-line 'r)
                          )))
          (concat (powerline-render lhs)
                  (powerline-fill face2 (powerline-width rhs))
                  (powerline-render rhs))))))
    )
  ;; (my:powerline-theme)
  )

Debug&Test

(leaf treesit
  :config
  (setq treesit-font-lock-level 4)
  )
(leaf treesit-auto
  :ensure t
  :init
  (setq treesit-auto-install 'prompt)
  :config
  (treesit-auto-add-to-auto-mode-alist 'all)
  :hook
  (emacs-startup-hook . global-treesit-auto-mode)
  )
(leaf enh-ruby-mode
  ;; :ensure t
  :disabled t
  :advice
  ((:override ruby-mode enh-ruby-mode))
  )

(defun my:debug-on-quit-if-scratch (&rest _args)
  (setq debug-on-quit (string= (buffer-name) "*scratch*")))
(add-hook 'window-selection-change-functions 'my:debug-on-quit-if-scratch)
(leaf which-key
  :ensure t
  :init
  (setq which-key-show-early-on-C-h t
        which-key-idle-delay 10000
        which-key-idle-secondary-delay 0.05
        which-key-popup-type 'minibuffer
        )
  :hook (emacs-startup-hook . which-key-mode)
  )
;; (leaf tree-sitter :ensure t)
;; (leaf tree-sitter-langs :ensure t)
(leaf rg
  :if (executable-find "rg")
  :ensure t
  )
;;
;; (leaf mozc-temp
;;   :init
;;   (leaf mozc
;;     :config
;;     (setq default-input-method "japanese-mozc"))
;;   :bind* ("C-j" . mozc-temp-convert)
;;   :require t
;;   )
;; (global-set-key (kbd "C-j") #'mozc-temp-convert)
;;(leaf emacs
;;  :preface
;;  (defun my-advice/window-width (fn &rest args)
;;    (- (apply fn args) 1))
;;  :advice (:around window-width my-advice/window-width))
;;
;; (leaf elfeed
;;   :if (file-directory-p my:d:password-store)
;;   :ensure t
;;   :custom
;;   `((elfeed-set-timeout  . 36000)
;;     (elfeed-db-directory . "~/.cache/elfeed"))
;;   :config
;;   (leaf elfeed-goodies
;;     :ensure t
;;     :config
;;     (elfeed-goodies/setup))
;;   ;;
;;   (leaf elfeed-protocol
;;     :ensure t
;;     :config
;;     (setq elfeed-feeds
;;           '(("owncloud+https://uwabami@uwabami.junkhub.org/nextcloud"
;;              :password (password-store-get "Web/uwabami.junkhub.org/nextcloud")
;;              )
;;             ))
;;     (elfeed-protocol-enable)
;;     )
;;   )
;;
(leaf vterm
  :ensure t
  :init
  (setq vterm-always-compile-module t)
  :hook
  (vterm-mode-hook
   . (lambda () (setq-local global-hl-line-mode nil)))
  )
(leaf nginx-mode :ensure t)
(leaf apache-mode :ensure t)
(leaf keg :ensure t)
(leaf keg-mode :ensure t)
(leaf esup
  :ensure t
  :init
  (setq esup-insignificant-time 0.01
        esup-depth 0 ;; 🤔
        )
  )

最後に

profiler report

必要に応じて
(setq debug-on-error nil)
;; (profiler-report)
;; (profiler-stop)

provide の設定

(provide 'init)
;; Local Variables:
;; byte-compile-warnings: (not cl-functions free-vars docstrings unresolved)
;; End:
;; Local Variables:
;; byte-compile-warnings: (not cl-functions free-vars docstrings unresolved)
;; End:

LICENSE

幾つかの関数の元ネタとして Emacs 本体のコードを参照したので, GPL-3 or later です.
Copyright (C) 2011--2017 Youhei SASAKI <uwabami@gfd-dennou.org>
.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
.
This package is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.

About

Emacs の設定ファイル群

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published