Emacs の設定


Related Index Emacs

はじめに

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

基本方針は以下の通り:

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

:Eating your own dog food - Wikipedia

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

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

設定にはleaf.elを利用します. VCS からインストールしたいパッケージが幾つかあるので, それらについてはel-getを利用しています.

設定は Org mode で書きたい

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

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

EMACS   ?= emacs
init.el: README.org
    $(EMACS) -Q -q --batch --eval \
       "(progn \
          (require 'ob-tangle) \
          (org-babel-tangle-file \"$<\" \"$@\" \"emacs-lisp\"))"
    $(EMACS) -q -l init.el --batch --eval '(kill-emacs)'
%.elc: %.el
    $(EMACS) -q -l init.el -batch -f batch-byte-compile $<

設定ファイルのヘッダ

出力される init.el 用のヘッダは以下の通り. lexsical-binding を有効にしておきます.

;; -*- lexical-binding: nil -*-

起動の高速化

この辺は Emacs の起動時間を""詰める"" を参考に. 設定は early-init.el に移動した方が良いのかもしれないかな.

Emacs Profiler

起動の高速化を検証するために, 必要に応じて有効に.

;; (require 'profiler)
;; (profiler-start 'cpu)

Magic File Name を一時的に無効化

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

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

GC の設定

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

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

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

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

(defconst my:default-gc-cons-threshold gc-cons-threshold)
(setq gc-cons-threshold most-positive-fixnum)
;; 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 my:default-gc-cons-threshold)))

early-init.el

Emacs >= 27 からの機能だが, いまいち使い方がわからん.

(push '(vertical-scroll-bars . nil) default-frame-alist)
(push '(menu-bar-lines       . nil) default-frame-alist)
(push '(tool-bar-lines       . 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 load-prefer-newer noninteractive)
(setq frame-inhibit-implied-resize t)
(setq site-run-file nil)
;; (Setq window-divider-default-right-width 3)
(setq package-enable-at-startup nil)
(provide 'early-init)
;;; early-init.el ends here

ディレクトリ構成の修正

分割した設定ファイル群やパッケージでinstallしたパッケージ の置き場所は user-emacs-directory 以下にまとめています。

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

    ~/.emacs.d/
     |-- Makefile    ←  byte-compile 用の rule
     |-- README.org  ←  本ファイル.`org-babel-tangle' で init.el を生成
     |-- pkg
     |   |-- elpa/   ←  package.el で導入したパッケージが置かれる場所
     |   `-- el-get/ ←  el-get で導入したパッケージが置かれる場所
     `-- share/      ←  (基本的に)参照するだけの資源置き場所
    ~/.cache/emacs   ←  一次ファイルの置き場所

上記ディレクトリ構成を設定ファイルで使用するために ディレクトリ配置を宣言しておきます。

(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")))
(unless (file-directory-p my:d:tmp)
  (make-directory my:d:tmp))
(defconst my:d:pkg:elpa
  (expand-file-name "pkg/elpa" user-emacs-directory))
(defconst my:d:pkg:elget
  (expand-file-name "pkg/el-get" user-emacs-directory))

その他, 良く使うディレクトリもここで設定しておきます.

(defconst my:d:nextcloud (concat (getenv "HOME") "/Nextcloud"))
(defconst my:d:org (expand-file-name "org" my:d:nextcloud))

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

幾つかの関数で Common-Lisp 的挙動が期待されているので, cl-lib を読み込んでおきます.

(eval-when-compile (require 'cl-lib nil t))

ついでに Emacs 27 以降は cl が読み込まれていると `Package cl is deprecated' が表示されるので, これを止めておく.

(setq byte-compile-warnings '(not cl-functions obsolete))

Package 関連: package.el, leaf.el, el-get

leaf.elのおかげで, 無いと途方に暮れるパッケージ以外のインストールは無視できるようになります.

package.el

パッケージは基本的に package.el で導入するので, 先ずはその設定.

;; elpa/gnutls workaround
(eval-and-compile
  (when (version<=  emacs-version "26.2")
    (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
  (custom-set-variables
   '(package-archives '(("gnu"   . "https://elpa.gnu.org/packages/")
                        ("melpa" . "https://melpa.org/packages/")
                        ("org" . "https://orgmode.org/elpa/")
                        ))
   '(package-gnupghome-dir (expand-file-name ".gnupg" (getenv "HOME")))
   '(package-user-dir my:d:pkg:elpa))
  )
(eval-when-compile
  (unless (file-exists-p (expand-file-name "bootstrap-stamp" my:d:tmp))
    (package-refresh-contents)
    (with-temp-buffer
      (write-file (expand-file-name "bootstrap-stamp" my:d:tmp)))
    ))
(package-initialize)

leaf.el

個々のパッケージの設定にはleaf.elを利用します. 自分で修正した版やオリジナル版を別の場所から持ってくる場合は leaf.elからel-getを呼び出します.

(unless (package-installed-p 'leaf)
  (package-refresh-contents)
  (package-install 'leaf t))
;; (require 'leaf nil 'noerror)
;; (defvar leaf--load-file-name nil)
(leaf leaf-keywords
  :ensure t
  :init
  (leaf blackout :ensure t)
  (leaf el-get
    :ensure t
    :preface
    (defconst el-get-dir my:d:pkg:elget) ;; override el-get default
    :custom ((el-get-notify-type       . 'message)
             (el-get-git-shallow-clone . t))
    )
  )
(leaf-keywords-init)

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

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 exec-path-from-shell
  :ensure t
  :defun (exec-path-from-shell-initialize)
  :custom
  ((exec-path-from-shell-check-startup-files . nil)
   (exec-path-from-shell-arguments . nil)
   (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"
        )))
  :config
  (exec-path-from-shell-initialize)
  (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")) ""))
  )

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

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

といった事をしている.

(leaf *authentication
  :if (and (getenv "GPG_KEY_ID")
           (file-directory-p my:d:password-store))
  :init
  (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))
  (leaf auth-source
    :custom
    `((auth-source-gpg-encrypt-to . '(getenv "GPG_KEY_ID"))
      ;; (auth-sources
      ;; . ,(expand-file-name "authinfo.gpg" my:d:password-store))
      )
    )
  (leaf password-store :ensure t)
  (leaf auth-source-pass :ensure t)
  (leaf plstore
    :custom
    `((plstore-secret-keys . 'silent)
      (plstore-encrypt-to  . ,(getenv "GPG_KEY_ID")))
    )
  )

独自関数

細かい独自関数, など.

ファイル名を 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)
                     (< (length (concat "/" (car path) output)) vmax)))
      (setq output (concat "/" (car path) output))
      (setq path (cdr path)))
    ;; 省略
    (when path
      (setq output (concat "/..." output)))
    (format "%s%s" top output)))

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

ゴミが残らないし, 地味に便利.

(leaf *delete-file-if-no-contents
  :preface
  (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)))))
  :hook
  (after-save-hook . my:delete-file-if-no-contents)
  )

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

…元ネタがどこだったのか忘れてしまった…

(leaf *keepscratchbuffer
  :preface
  (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")))))
  (defun my:buffer-name-list ()
    "buffer 一覧の取得"
    (mapcar (function buffer-name) (buffer-list)))
  ;;
  :hook
  ((kill-buffer-query-functions
    . (lambda ()
        (if (string= "*scratch*" (buffer-name))
            (progn (my:make-scratch 0) nil)
          t)))
   (after-save-hook
    . (lambda ()
        (unless (member "*scratch*" (my:buffer-name-list))
          (my:make-scratch 1)))))
  )

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

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

ただし, RD や Markdown だと空白行に意味があったりするので, 必要に応じて拡張子で判断して外している.

(leaf *trailing-white-space
  :preface
  (defvar my:delete-trailing-whitespace-exclude-suffix
    (list "\\.rd$" "\\.md$" "\\.rbt$" "\\.rab$"))
  (defun my:delete-trailing-whitespace ()
    (interactive)
    (eval-when-compile (require 'cl-lib))
    (cond
     ((equal nil
             (cl-loop for pattern in my:delete-trailing-whitespace-exclude-suffix
                      thereis (string-match pattern buffer-file-name)))
      (delete-trailing-whitespace))))
  :hook
  (before-save-hook . my:delete-trailing-whitespace)
  )
  • [ ] cl 依存の書き換え?

ターミナルで 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- に解釈させるように設定しておく.

; cargo cult adaptation of event-apply-control-modifier
(defun my:event-apply-control-meta-modifiers (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)

言語の設定

最近の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)
  )

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

終了時に custom.el を消す

設定ファイルに極力移す.

(leaf cus-edit
  :preface
  (setq custom-file (expand-file-name "custom.el" my:d:tmp))
  :custom
  `((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)))))
  )

customize で設定していたアレコレ

custom.el にある設定は極力こちらに移すようにしている.

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

他にもイロイロと. 設定が増えてきたら分ける.

(leaf cus-start
  :custom
  `(
    ;; 表示
    (ring-bell-function     . 'ignore)   ; ベル無効化
    ;; 編集
    (tab-width              . 4)    ;; tab 幅 4
    (indent-tabs-mode       . nil)  ;; tab ではインデントしない
    (fill-column            . 72)   ;; RFC2822 風味
    (truncate-lines         . nil)  ;; 折り返し無し
    (truncate-partial-width-windows . nil)
    (paragraph-start        . '"^\\([  ・○<\t\n\f]\\|(?[0-9a-zA-Z]+)\\)")
    (auto-fill-mode         . nil)
    (next-line-add-newlines . nil)  ;; バッファ終端で newline を入れない
    (read-file-name-completion-ignore-case . t)  ; 大文字小文字区別無し
    (save-abbrevs           . 'silent)
    ;; backup
    (auto-save-list-file-prefix . ,(expand-file-name ".saves-" my:d:tmp))
    (auto-save-default       . t)
    (auto-save-timeout       . 15)
    (auto-save-interval      . 60)
    (make-backup-files       . t)
    (backup-by-copying       . t)  ;; symlink は使わない
    (backup-directory-alist  . '(("." . ,my:d:tmp)))
    (auto-save-file-name-transforms . '((".*" ,my:d:tmp t)))
    (version-control         . nil)
    (kept-new-versions       . 2)
    (kept-old-versions       . 2)
    (delete-old-versions     . t)
    (delete-auto-save-files  . t)
    ;; undo/redo - 数字に根拠無し
    (undo-limit              . 200000)
    (undo-strong-limit       . 260000)
    (history-length          . t)  ;; 無制限(の筈)
    ;; (save-silently           . t)
    ;;
    (safe-local-variable-values
     . '((org-link-file-path-type . absolute)))
    )
  :config
  (when (boundp 'load-prefer-newer)
    (setq load-prefer-newer t))
  ;; yes or no を y or n に
  (fset 'yes-or-no-p 'y-or-n-p)
  )

startup: 起動は静かに

(leaf startup
  :custom
  ((inhibit-startup-screen            . t)
   (inhibit-startup-message           . t)
   (inhibit-startup-echo-area-message . t)
   (initial-scratch-message           . nil)
   )
  )

hl-mode: 現在行のハイライト

(leaf hl-line
  :hook
  (emacs-startup-hook . global-hl-line-mode)
  )

選択リージョンに色付け

(leaf simple
  :hook
  (emacs-startup-hook . transient-mark-mode)
  )

show-paren-mode: 対応する括弧を強調表示

(leaf paren
  :custom
  ((show-paren-style  . 'mixed))
  :hook
  (emacs-startup-hook . show-paren-mode)
  )

linum-mode : 行番号表示

必要に応じて有効にするので, 基本使わない. 通常はモードラインに行番号や桁番号を表示しないようする. ついでに linum-mode を有効にした場合の桁表示を 5 桁に.

(leaf line-number-mode
  :custom
  ((linum-format     . "%5d ")
   (line-number-mode . nil))
  )

byte-compile 関連

  • debug は表示しない: 必要に応じて t に変更する
  • Compile-Log の非表示: ほとんど見ないし.
  • Warning の抑制: これもほとんど見ないし.

他にも増えそうだが

(eval-and-compile
  (leaf bytecomp
    :custom
    ((byte-compile-warnings . '(not
                                obsolete
                                free-vars
                                unresolved
                                callargs
                                redefine
                                noruntime
                                cl-functions
                                interactive-only
                                make-local))
     (debug-on-error        . nil))
    :config
    (let ((win (get-buffer-window "*Compile-Log*")))
      (when win (delete-window win)))
    )
  )

autorevert: ファイルが変更されたら再読み込み

(leaf autorevert
  :custom
  ((auto-revert-interval . 0.1))
  :hook
  (emacs-startup-hook . global-auto-revert-mode)
  )

savehist: 変更履歴を保存

(leaf savehist
  :custom
  `((savehist-file
     . ,(expand-file-name "history" my:d:tmp)))
  :hook
  ((after-init-hook . savehist-mode))
  )

ファイル, デイレクトリ整理

~/.emacs.d/ 以下にファイルが転がるのがなんか嫌なので, 気がつく度に設定している.

(leaf *change-default-file-location
  :custom
  `(;; url
    (url-configuration-directory
     . ,(expand-file-name "url" my:d:tmp))
    ;; nsm
    (nsm-settings-file
     . ,(expand-file-name "nsm.data" my:d:tmp))
    ;; bookmark
    (bookmark-default-file
     . ,(expand-file-name "bookmarks" my:d:tmp))
    ;; eshell
    (eshell-directory-name
     . ,(expand-file-name "eshell" my:d:tmp))
    )
  )

他にもイロイロありそう. bookmark はちゃんと使いこなしたい所ではあるが.

eldoc: emacs-lisp document

minibuffer では eldoc にお黙り頂く。

(leaf eldoc
  :hook (emacs-lisp-mode-hook . turn-on-eldoc-mode)
  :blackout t
  :preface
  (defun my:shutup-eldoc-message (f &optional string)
    (unless (active-minibuffer-window)
      (funcall f string)))
  :advice
  (:around eldoc-message
           my:shutup-eldoc-message)
  )

midnight: 一定期間使用しなかった buffer を自動削除

(leaf midnight
  :custom
  ((clean-buffer-list-delay-general . 1))
  :hook
  (emacs-startup-hook . midnight-mode))

uniquify: モードラインのファイル名にディレクトリも表示する

(leaf uniquify
  :custom
  ((uniquify-buffer-name-style . 'post-forward-angle-brackets)
   (uniquify-min-dir-content   . 1))
  )

whitespace: 空白の強調表示

背景も変えようかなぁ…

(leaf whitespace
  :blackout ((global-whitespace-mode . "")
             (whitespace-mode        . ""))
  :hook (after-init-hook . global-whitespace-mode)
  :custom
  ((whitespace-line-column      . 72)
   (whitespace-style
    . '(face        ; faceを使う
        trailing    ; 行末の空白を対象.
        tabs        ; tab
        spaces      ; space
        ))
   (whitespace-display-mappings . '((space-mark ?\u3000 [?\□])
                                    (tab-mark ?\t [?\u00BB ?\t] [?\\ ?\t])))
   (whitespace-space-regexp     . "\\(\u3000+\\)")
   (whitespace-global-modes     . '(not eww-mode
                                        term-mode
                                        eshell-mode
                                        org-agenda-mode
                                        calendar-mode))
   )
  )

saveplace: 前回の修正位置を記憶する.

記憶の保存先を ~/.emacs.d/tmp/emacs-places に変更.

(leaf save-place
  :custom
  `((save-place . t)
    (save-place-file
     . ,(expand-file-name "emacs-places"  my:d:tmp))
    )
  :hook (emacs-startup-hook . save-place-mode)
  :config
  (setq save-place-ingore-files-regexp
        (format "\\(%s\\)\\|\\(%s\\)"
                save-place-ignore-files-regexp
                tramp-file-name-regexp))
  )

time-stamp: 保存時に timestamp を自動更新

デフォルトではいろいろと衝突したので 更新文字列を変更し, $Lastupdate: 2 ($は半角) があったら timestamp を更新する様にした.

(leaf time-stamp
  :hook (before-save-hook . time-stamp)
  :custom
  ((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")
   )
  )

モード独自の設定(例えば Org とか)に関しては別途.

tramp: ssh 越しにファイルを編集

(leaf tramp
  :preface
  (setq tramp-persistency-file-name (expand-file-name "tramp" my:d:tmp))
  :custom
  `((tramp-persistency-file-name
     . ,(expand-file-name "tramp" my:d:tmp))
    (tramp-completion-reread-directory-timeout . nil)
    )
  :hook
  (kill-emacs-hook
   . (lambda ()
       (if (file-exists-p tramp-persistency-file-name)
           (delete-file tramp-persistency-file-name))))
  )

browse-url

ブラウザ呼び出しは xdg-open/open に丸投げ.

(leaf browse-url
  :require t
  :bind ("C-c C-j" . browse-url-at-point)
  :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 'w3m-browse-url)
         (setq browse-url-browser-function 'eww-browse-url)
         ))
  )

server: Emacs server

(leaf server
  :commands (server-running-p)
  :init
  (defun my:new-client-frame ()
    "Create new GUI emacsclient"
    (interactive)
    (make-frame-on-display (getenv "DISPLAY")))
  :hook
  (emacs-startup-hook . (lambda ()
                          (unless (server-running-p)
                            (server-start))))
  )

buffer の印刷

(leaf ps-mule
  :custom
  ((ps-multibyte-buffer . 'non-latin-printer))
  :config
  (defalias 'ps-mule-header-string-charset 'ignore)
  )

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)
  ;;
  (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))))
  :custom
  ((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-new-button-show        . nil)
   (tab-bar-tab-name-function      . 'my:tab-bar-tab-name-truncated)
   (tab-bar-tab-name-truncated-max . 12)
   (tab-bar-separator              . "")
   )
  :config
  (tab-bar-mode +1)
  )

beacon: buffer 切り替え時の行の強調表示

(leaf beacon
  :ensure t
  :disabled t
  :custom
  `((beacon-color              . "#aa3400")
    ;; (beacon-size               . 64)
    (beacon-blink-when-focused . t)
    )
  :custom-face
  `((beacon-fallback-background . '((t (:background "#556b2f")))))
  :config
  (beacon-mode 1)
  )

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

結局履歴を貯める設定をしている事になっている. ディレクトリの履歴も取れるので recentf-ext を入れておく

(leaf recentf
  :defun
  (recentf-save-list recentf-cleanup)
  :preface
  (leaf shut-up
    :ensure t
    :init
    (defvar shut-up-ignore t))
  ;;
  (defun my:recentf-save-list-silence ()
    "Shut up"
    (interactive)
    (let ((message-log-max nil))
      (shut-up (recentf-save-list)))
    (message ""))
  ;;
  (defun my:recentf-cleanup-silence ()
    "Shut up"
    (interactive)
    (let ((message-log-max nil))
      (shut-up (recentf-cleanup)))
    (message ""))
  ;;
  :init
  (leaf recentf-ext :ensure t)
  :hook
  ((after-init-hook . recentf-mode)
   (focus-out-hook  . my:recentf-save-list-silence)
   (focus-out-hook  . my:recentf-cleanup-silence))
  :custom
  `((recentf-save-file       . ,(expand-file-name "recentf" my:d:tmp))
    (recentf-max-saved-items . 2000)
    (recentf-auto-cleanup    . 'never)
    (recentf-exclude         . '(".recentf"
                                 "^/tmp\\.*"
                                 "^/private\\.*"
                                 "^/var/folders\\.*"
                                 "/TAGS$"
                                 "\\.*草稿\\.*"
                                 "^#\\.*"
                                 "^/home/uwabami/.mozilla/\\.*"
                                 "^/home/uwabami/.emacs.d/tmp/\\.*"
                                 "^/home/uwabami/.dotfiles/Emacs/tmp/\\.*"
                                 "^/[^/:]+:"
                                 "bookmarks"
                                 "org-recent-headings.dat"
                                 "^/mnt/c/\\.*"
                                 "\\.*COMMIT_EDITMSG$"
                                 ".*-autoloads.el$"
                                 "^/home/uwabami/.emacs.d/pkg/\\.*"
                                 )))
  )

all-the-icons-in-terminal: ターミナルでもicon fontを使いたい。

all-the-icons.elのデータを修正して, icons in terminalを修正した 自作フォントのデータを読みに行くようにしてみました。

(leaf all-the-icons
  :ensure t
  :init (leaf memoize :ensure t)
  :require t
  :custom
  ((all-the-icons-scale-factor   . 0.9)
   (all-the-icons-default-adjust . 0.0))
  )
(leaf all-the-icons-in-terminal
  :el-get (all-the-icons-in-terminal
           :type github
           :pkgname "uwabami/isfit-plus")
  :after all-the-icons
  :require t
  :config
  (add-to-list 'all-the-icons-mode-icon-alist
               '(f90-mode all-the-icons-faicon "facebook")) ;; facebook!?
  (add-to-list 'all-the-icons-mode-icon-alist
               '(wl-folder-mode all-the-icons-faicon "folder-o" ))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(wl-summary-mode all-the-icons-faicon "folder-open-o"))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(wl-draft-mode all-the-icons-material "drafts"))
  (add-to-list 'all-the-icons-mode-icon-alist
               '(mime-view-mode all-the-icons-faicon "envelope-o"))
  )

SOMEDAY East Asian Ambiguos 対応 [0/1]

East Asian Ambiguosを2文字幅にして, ついでに CJK 以外の East Asian Ambiguosと絵文字も2文字幅にするようにしています。 拙作の修正ロケールはこちら: https://github.com/uwabami/locale-eaw-emoji

(leaf locale-eaw-emoji
  :el-get uwabami/locale-eaw-emoji
  :after all-the-icons-in-terminal
  :require t
  :config
  ;; (eaw-half-emoji-fullwidth)
  (eaw-and-emoji-fullwidth)
  )
  • [ ] 最近, EAWは一文字幅強制の方が良いかなぁ, とか悩み中.

macOS対応

最近良く触る様になったので設定している。 まあ, イマイチ慣れない訳ですけれど

(leaf *mac-encoding
  :if (eq system-type 'darwin)
  (leaf ucs-normalize
    :require t
    :defvar (mac-pass-control-to-system ns-command-modifier ns-alternate-modifier)
    :config
    (set-file-name-coding-system 'utf-8-hfs)
    (setq locale-coding-system 'utf-8-hfs)
    (setq mac-pass-control-to-system t  ;; Ctrl を Mac から奪い取る
          ns-command-modifier 'meta     ;; Cmd と Option を逆にする
          ns-alternate-modifier 'super)
    (global-set-key [ns-drag-file] 'ns-find-file)
    )
  )

Windows(WSL2)対応

(leaf *wsl2-path
  :if (getenv "WSL_DISTRO_NAME")
  :preface
  (defun set-drvfs-alist ()
    (interactive)
    (setq drvfs-alist
          (mapcar
           (lambda (x)
             (when (string-match "\\(.*\\)|\\(.*?\\)/?$" x)
               (cons (match-string 1 x) (match-string 2 x))))
           (split-string (concat
                          ;; //wsl$ パス情報の追加
                          (when (or (not (string-match "Microsoft" (shell-command-to-string "uname -v")))
                                    (>= (string-to-number (nth 1 (split-string operating-system-release "-"))) 18362))
                            (concat "/|" (shell-command-to-string "wslpath -m /")))
                          (shell-command-to-string
                           "mount | grep -E ' type (9p|drvfs) ' | grep -v '^tools on /init type 9p' | sed -r 's/(.*) on (.*) type (9p|drvfs) .*/\\2\\|\\1/' | sed 's!\\\\!/!g'"))
                         "\n" t))))
  ;;
  (defconst windows-path-style-regexp "\\`\\(.*/\\)?\\([a-zA-Z]:\\\\.*\\|[a-zA-Z]:/.*\\|\\\\\\\\.*\\|//.*\\)")
  ;;
  (defun windows-path-convert-file-name (name)
    (setq name (replace-regexp-in-string windows-path-style-regexp "\\2" name t nil))
    (setq name (replace-regexp-in-string "\\\\" "/" name))
    (let ((case-fold-search t))
      (cl-loop for (mountpoint . source) in drvfs-alist
               if (string-match (concat "^\\(" (regexp-quote source) "\\)\\($\\|/\\)") name)
               return (replace-regexp-in-string "^//" "/" (replace-match mountpoint t t name 1))
               finally return name)))
  ;;
  (defun windows-path-run-real-handler (operation args)
    "Run OPERATION with ARGS."
    (let ((inhibit-file-name-handlers
           (cons 'windows-path-map-drive-hook-function
                 (and (eq inhibit-file-name-operation operation)
                      inhibit-file-name-handlers)))
          (inhibit-file-name-operation operation))
      (apply operation args)))
  ;;
  (defun windows-path-map-drive-hook-function (operation name &rest args)
    "Run OPERATION on cygwin NAME with ARGS."
    (windows-path-run-real-handler
     operation
     (cons (windows-path-convert-file-name name)
           (if (stringp (car args))
               (cons (windows-path-convert-file-name (car args))
                     (cdr args))
             args))))
  :hook
  (after-init-hook
   . (lambda ()
       (progn
         (set-drvfs-alist)
         (add-to-list 'my:saved-file-name-handler-alist
                      (cons windows-path-style-regexp
                            'windows-path-map-drive-hook-function)))))
  )

カレンダー設定

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

(leaf calendar
  :custom
  (;; 祝日をカレンダーに表示
   (mark-holidays-in-calendar . t)
   ;; 月と曜日の表示調整
   (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))
  :config
  (with-eval-after-load 'calendar
    (leaf japanese-holidays
      :ensure t
      :custom
      ((japanese-holiday-weekend         . '(0 6))
       (japanese-holiday-weekend-marker  . '(holiday  ;; 日
                                             nil      ;; 月
                                             nil      ;; 火
                                             nil      ;; 水
                                             nil      ;; 木
                                             nil      ;; 金
                                             japanese-holiday-saturday)))
      :hook
      ((calendar-today-visible-hook   . japanese-holiday-mark-weekend)
       (calendar-today-invisible-hook . japanese-holiday-mark-weekend)
       (calendar-today-visible-hook   . calendar-mark-today))
      :config
      (setq calendar-holidays (append japanese-holidays))
      )
    )
  )

キーバインドの設定

既に手癖になってしまっているアレコレ. 特に [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
  ;; :require t
  :custom
  '((migemo-user-dictionary  . nil)
    (migemo-regex-dictionary . nil)
    (migemo-options          . '("-q" "--emacs"))
    (migemo-command          . "cmigemo")
    (migemo-coding-system    . 'utf-8-unix))
  :init
  (cond
   ((and (eq system-type 'darwin)
         (file-directory-p "/usr/local/share/migemo/utf-8/"))
    (setq migemo-dictionary "/usr/local/share/migemo/utf-8/migemo-dict"))
   (t
    (setq migemo-dictionary "/usr/share/cmigemo/utf-8/migemo-dict")))
  :hook
  (after-init-hook . migemo-init)
  )

emacs-w3m:

(leaf emacs-w3m
  :if (executable-find "w3m")
  ;;:el-get (emacs-w3m
  ;;         :type github
  ;;         :pkgname "emacs-w3m/emacs-w3m"
  ;;         :build `(("autoconf")
  ;;                  ("./configure" ,(format "--with-emacs=%s" el-get-emacs))
  ;;                  ("make"))
  ;;         :info "doc")
  :load-path
  `(,(expand-file-name "emacs-w3m/shimbun" my:d:pkg:elget))
  )

SOMEDAY eww: 内蔵ブラウザ [0/3]

リンクを簡単に辿る(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 addressbar
    :el-get (addressbar
             :type github
             :pkgname "lurdan/emacs-addressbar")
    :custom
    `((addressbar-persistent-history-directory
       . ,(expand-file-name my:d:tmp))
      (addressbar-ignore-url-regexp
       . "\\(://duckduckgo\\.com/\\|google\\.com/search\\)")
      (addressbar-search-command-alist
       .  '("g" . "https://google.com/search?&gws_rd=cr&complete=0&pws=0&tbs=li:1&q="))
      (addressbar-display-url-max-length . 60)
      ))
  (leaf shr
    :custom
    ((shr-use-colors    . nil)
     (shr-use-fonts     . nil)
     (shr-image-animate . nil)
     (shr-width         . 72))
    )
  :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)
          ("+"   . my:eww-increase-width)
          ("-"   . my:eww-decrease-width)
          ("h"   . backward-char)
          ("j"   . next-line)
          ("k"   . previous-line)
          ("l"   . forward-char)
          ("/"   . isearch-forward)
          ("?"   . isearch-backward)
          ("n"   . isearch-next)
          ("N"   . isearch-previous)
          ("f"   . ace-link-eww))
         )
  :custom
  `((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="))
  :config
  (ace-link-setup-default)
  ;; :init
  ;; ;;
  ;; (defun eww-disable-images ()
  ;;   "ewwで画像表示させない"
  ;;   (interactive)
  ;;   (setq-local shr-put-image-function 'shr-put-image-alt)
  ;;   (eww-reload))
  ;; ;;
  ;; (defun eww-enable-images ()
  ;;   "ewwで画像表示させる"
  ;;   (interactive)
  ;;   (setq-local shr-put-image-function 'shr-put-image)
  ;;   (eww-reload))
  ;; (defun shr-put-image-alt (spec alt &optional flags)
  ;;   (insert alt))
  ;; ;;
  ;; ;;
  ;; (defun eww-mode-hook--disable-image ()
  ;;   (setq-local shr-put-image-function 'shr-put-image-alt))
  ;; (add-hook 'eww-mode-hook 'eww-mode-hook--disable-image)
  )
  • [ ] 背景色の指定
  • [ ] 幅の強制

日本語入力: ddskk

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

! disable XIM
Emacs*useXIM: false

Emacs 本体側の設定(ddskk)

実際の設定は別ファイルで行なわれるため ここでは設定ファイルの位置変更を変更している.

(defvar skk-user-directory (concat my:d:tmp "skk"))
(unless (file-directory-p skk-user-directory)
  (make-directory skk-user-directory))
(unless (locate-library "skk")
  (package-install 'ddskk t))
(leaf skk
  :commands skk-make-indicator-alist
  :bind (("C-x j"   . skk-mode)
         ("C-x C-j" . skk-mode)
         ("C-\\"    . skk-mode))
  :init
  (setq skk-init-file (concat user-emacs-directory "init-ddskk")
        default-input-method "japanese-skk" )
  )

DDSKK 本体の設定

基本動作

byte-compile の為の読み込み

(eval-when-compile (require 'skk))

sticky shift: sticky shift を参照のこと. ddskk の 14.2 以降から同梱されるようになった(ありがたい)

(setq skk-sticky-key ";")

変換候補の表示位置

(setq skk-show-candidates-always-pop-to-buffer nil)

候補表示件数を2列に

(setq skk-henkan-number-to-display-candidates 5)

日本語表示しない

(setq skk-japanese-message-and-error nil)

メニューを日本語にしない -> toolbar 非表示だし.

(setq skk-show-japanese-menu nil)

注釈の表示

(setq skk-show-annotation nil)

インジケータの表示のカスタマイズ

(setq skk-latin-mode-string "[_A]")
(setq skk-hiragana-mode-string "[あ]")
(setq skk-katakana-mode-string "[ア]")
(setq skk-jisx0208-latin-mode-string "[A]")
(setq skk-jisx0201-mode-string "[_ア]")
(setq skk-abbrev-mode-string "[aA]")
(setq skk-indicator-use-cursor-color nil)

インジケータを左端に表示

(setq skk-status-indicator 'left)

mode-line が動くのが許せないので, ちょっと修正

(defadvice skk-make-indicator-alist
    (after my:set-skk-default-indicator activate)
  (dolist (elem
           '((abbrev " [aA]" . "--[aA]:")
             (latin " [_A]" . "--[_A]:")
             (default " [--]" . "--[--]:"))
           )
    (setq ad-return-value
          (append (cons elem nil)
                  (delq (assoc (car elem) ad-return-value) ad-return-value)))))
;;
;;(setq skk-show-inline t)

カーソルには色をつけない

(setq skk-use-color-cursor nil)

キーバインド

(global-set-key "\C-x\C-j" 'skk-mode)
(global-set-key "\C-xj" 'skk-mode)
(global-set-key "\C-j" 'skk-mode)
(global-set-key "\C-\\" 'skk-mode)

半角カナを入力

(setq skk-use-jisx0201-input-method t)

Enter で改行しない

(setq skk-egg-like-newline t)

"「"を入力したら"」"も自動で挿入

(setq skk-auto-insert-paren t)

句読点変換ルール

(setq skk-kuten-touten-alist
      '(
        (jp    . ("。" . "、"))
        (jp-en . ("。" . ", "))
        (en-jp . ("." . ","))
        (en    . (". " . ", "))
        ))
(setq-default skk-kutouten-type 'en)

全角記号の変換: @ での日付入力は使わない

(setq skk-rom-kana-rule-list
      (append skk-rom-kana-rule-list
              '(("!" nil "!")
                (":" nil ":")
                (";" nil ";")
                ("?" nil "?")
                ("z " nil " ")
                ("\\" nil "\\")
                ("@" nil "@")
                )))

送り仮名が厳密に正しい候補を優先

(setq skk-henkan-strict-okuri-precedence t)

辞書の共有

(setq skk-share-private-jisyo t)

変換候補を縦に表示

;; (setq skk-show-inline 'vertical)
(setq skk-show-inline nil)

辞書の設定

追加している辞書の一覧は

といった所. はてなキーワードからの辞書の抽出は znz さんの

を参考に. MatsuCon で公開されている顔文字に関しては 顔文字に ; や が含まれている場合に, 適宜quoteする必要があるので 以下のスクリプトで適当に変換.

#!/usr/bin/env ruby
require 'nkf'
src = ARGV[0]
if ARGV.size < 1
  puts "usage: ime2skk.rb ime_dictionary"
  exit 0
end
File.open(src, "r") {|f|
  f.each do |line|
    line_euc = NKF.nkf("-S -e",line)
    if line_euc =~ /^([^!]+?)\t(.+?)\t.+$/
      entry = $1
      content = $2
      if content =~/;/
        puts entry + " /(concat \"" + content.gsub(';','\\\\073') + "\")/"
      elsif content =~/\//
        puts entry + " /(concat \"" + content.gsub('/','\\\\057') + "\")/"
      else
        puts entry + " /" + content + "/"
      end
    end
  end
}

他にも quote する必要あるような気もするけれど, それは気がついた時に.

辞書サーバがそもそも UTF-8 を扱えれば良いのだけれども. 辞書サーバの指定は以下.

;; ddskk <- yaskkserv2 のみ utf-8 で通信するための設定
(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)
(setq skk-mode-hook
      (lambda ()
        (advice-add 'skk-open-server :override 'my:skk-open-server-decoding-utf-8)))
(cond
 ((getenv "SKKSERVER")
  (setq skk-server-host (getenv "SKKSERVER")
        skk-server-portnum "1178"
        skk-large-jisyo nil)
  (add-to-list 'skk-search-prog-list
               '(skk-server-completion-search) t)
  (add-to-list 'skk-search-prog-list
               '(skk-comp-by-server-completion) t))
 (t
  (setq skk-get-jisyo-directory (concat my:d:tmp "skk-jisyo")
        skk-large-jisyo (concat skk-get-jisyo-directory "/SKK-JISYO.L"))
  (when (file-exists-p "/usr/local/share/skkdic/SKK-JISYO.emoji.utf8")
    (setq skk-extra-jisyo-file-list
          (list '("/usr/local/share/skkdic/SKK-JISYO.emoji.utf8" . utf-8)))))
 )

辞書登録の際に送り仮名を削除

(setq skk-check-okurigana-on-touroku 'auto)

漢字登録のミスをチェックする

(setq skk-check-okurigana-on-touroku t)

個人辞書の文字コード

(setq skk-jisyo-code 'utf-8-unix)

インクリメンタルサーチ

minibuffer 内では強制的に skk off. インクリメンタルサーチは migemo に任せることに.

(add-hook 'skk-mode-hook
          (lambda ()
            (and (skk-in-minibuffer-p)
                 (skk-mode-exit))))
(setq skk-isearch-start-mode 'latin)

校正, 辞書等

redpen-paragraph: redpen による文章校正

(leaf redpen-paragraph
  :if (and (executable-find "redpen")
           (file-directory-p "~/.config/redpen"))
  :ensure t
  :bind
  (("C-c C-r" . redpen-paragraph))
  :hook
  ((LaTeX-mode-hook
    . (lambda ()
        (setq redpen-commands
              '(
                ;; for english command
                "redpen -r json2 -c ~/.config/redpen/redpen-conf-en.xml -f latex %s 2>/dev/null"
                ;; for japanese command
                "redpen -r json2 -c ~/.config/redpen/redpen-conf-ja.xml -f latex %s 2>/dev/null"
                )))
    ))
  :init
  (defvar redpen-commands
    '(
      ;; for english command
      "redpen -r json2 -c ~/.config/redpen/redpen-conf-en.xml %s 2>/dev/null"
      ;; for japanese command
      "redpen -r json2 -c ~/.config/redpen/redpen-conf-ja.xml %s 2>/dev/null"
      ))
  ;;redpen-paragraph-force-reading-whole t
  )

ispell: spell checker

ispell はコマンドとして aspell を利用する.

(leaf ispell
  :if (file-executable-p "aspell")
  :custom
  (ispell-program-name . "aspell")
  :config
  (add-to-list 'ispell-skip-region-alist '("[^\000-\377]+"))
  )

flyspell: on-the-fly spell checker [0/1]

flyspell-mode は別途有効化しておいた方が良いのかもしれない

(leaf flyspell
  :ensure t
  :blackout (flyspell-mode . "F")
  :defun
  flyspell-emacs-popup-textual
  :preface
  (defun my:flyspell-popup-choose (orig event poss word)
    (if (window-system)
        (funcall orig event poss word)
      (flyspell-emacs-popup-textual event poss word)))
  :advice (:around flyspell-emacs-popup
                   my:flyspell-popup-choose)
  :hook
  ;; flyspell-prog-mode との switch が欲しい
  ((LaTeX-mode-hook . flyspell-mode))
  )
  • [ ] flyspell-prog-mode との switch が欲しい

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))
  :custom
  (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
  :el-get uwabami/text-adjust.el
  )

補完: vertico, marginalia, consult

最近話題になりだしたので, ちょっと使い始めてみた. 基本機能の拡張なので, 挙動が手に馴染む感じがとても良い.

helm, ivy の無効化

依存する拡張がまだまだ多いので, 一度インストールして邪魔しないようにしておくことに.

(leaf helm :ensure t :defer-config (helm-mode -1))
(leaf ivy :ensure t :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))))
  ;;
  (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)
  :bind
  (:vertico-map (("C-l" . my:filename-upto-parent)))
  :custom-face
  `((vertico-current
     . '((t (:inherit hl-line :background unspecified)))))
  :custom
  `((vertico-count . 9)
    (vertico-cycle . t)
    (vertico-multiline . '(("↓" 0 1
                            (face vertico-multiline))
                           ("…" 0 1
                            (face vertico-multiline))))
    )
  :config
  :hook (after-init-hook . vertico-mode)
  )

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

行揃えが微妙. あと, ファイル名を省略表示できないのかな? ⇒ Better truncation method for file names #70

(leaf marginalia
  :ensure t
  :bind (("M-A" . marginalia-cycle)
         (:minibuffer-local-map
          ("M-A" . marginalia-cycle)
          ))
  :custom
  `((marginalia-annotators
     . '(marginalia-annotators-light marginalia-annotators-heavy nil)))
  :hook
  (after-init-hook . marginalia-mode)
  )

consult: 便利コマンド集

とりあえず recetnf が使えないと途方に暮れるので consult-recent-file のカスタマイズのみ.

(leaf consult
  :ensure t
  :bind (("C-x C-r" . my:consult-recent-file))
  :custom
  `(;; 増やさないと preview 時に theme がロードされない模様.
    ;; とりあえず default の 10 倍にしている. 1 MB かな?
    (consult-preview-raw-size . 1024000)
    (consult-preview-key  . ,(kbd "C-M-p"))
    (consult-narrow-key   . "<")
    )
  :config
  (defun my:consult-recent-file ()
    "Find recent using `completing-read' with shorten filename"
    (interactive)
    (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)))))
  ;; :advice
  ;;   (:override register-preview
  ;;              consult-register-window)
  ;; :config
  ;;   (setq consult-preview-key (kbd "M-."))
  )

consult-ghq: ghq を consult で.

projectile かなんかを使う方が良さげだが, とりあえず.

(leaf consult-ghq
  :el-get uwabami/consult-ghq
  :bind (("C-x f" . consult-ghq-open))
  :custom
  `((consult-ghq-short-list . t))
  )

orderless: 補完候補の選択

イロイロと凝れそうだけど, とりあえずはデフォルトのままで.

(leaf orderless
  :ensure t
  ;; :init (leaf flx :ensure t)
  :custom
  `((completion-styles . '(orderless))
    (orderless-matching-styles
     . '(orderless-prefixes
         orderless-flex
         orderless-regexp
         orderless-initialism
         orderless-literal))
    )
  )

翻訳

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

Copy & Paste:

Linux では xclip を利用

clipboard と PRIMARY の同期には gpaste を使っている.

(leaf xclip
  :if (and (executable-find "xclip")
           (eq system-type 'gnu/linux))
  :ensure t
  :config
  (xclip-mode 1))

macOS では pbcopy/pbpaste を利用.

pbcopy/pbpase の呼び出し方が変わった? 動かない時がある様な。

(leaf *macOSclipborad
  :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)
  )

ibuffer: buffer の操作

buffer を眺めるのは ibuffer が好み

(leaf ibuffer
  :after all-the-icons-in-terminal
  :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))
         )
  :config
  (define-ibuffer-column icon (:name "  ")
    (let ((icon
           (if (and (buffer-file-name)
                    (all-the-icons-auto-mode-match?))
               (all-the-icons-icon-for-file
                (file-name-nondirectory (buffer-file-name)))
             (all-the-icons-icon-for-mode major-mode ))))
      (if (symbolp icon)
          (setq icon
                (all-the-icons-faicon
                 "file-o"
                 :face 'all-the-icons-dsilver))
        icon)))
  ;;
  (setq ibuffer-formats
        `((mark modified read-only
                " " (icon 2 2 :left :elide)
                ,(propertize " " 'display `(space :align-to 8))
                (name 18 18 :left :elide)
                " " (size 9 -1 :right)
                " " (mode 16 16 :left :elide) " " filename-and-process)
          (mark " " (name 16 -1) " " filename)))
  )

wanderulst: MUA の設定

MUA として Wanderlust を使っている

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)
  :preface
  (defun my:wl-mode-line-buffer-identification (&optional id)
    (force-mode-line-update t))
  (defconst my:d:wl-cache-directory
    (expand-file-name "wanderlust" "~/.cache"))
  (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))
  :advice (:override wl-mode-line-buffer-identification
                     my:wl-mode-line-buffer-identification)
  :custom
  `((elmo-msgdb-directory     . my:d:wl-cache-directory)
    (elmo-maildir-folder-path . my:d:wl-cache-directory)
    (elmo-cache-directory     . my:d:wl-cache-directory)
    (wl-score-files-directory . my:d:wl-cache-directory)
    (read-mail-command        . #'wl)
    (wl-init-file
     . ,(expand-file-name "init-wl" user-emacs-directory))
    (wl-demo                  . nil)
    )
  :init
  (define-mail-user-agent
    'wl-user-agent
    'wl-user-agent-compose
    'wl-draft-send
    'wl-draft-kill
    'mail-send-hook)
  )

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

Wanderlust 本体の設定

実際の設定は以下の通り

byte-compile の準備

(eval-and-compile
  (leaf el-x
    :el-get (el-x
             :type github
             :pkgname "sigma/el-x"
             :build `(("make" ,(format "EMACSBIN=%s" el-get-emacs)))
             :load-path "lisp"
             )
    :require t
    )
  )
(eval-when-compile
  (require 'cp5022x)
  (require 'wl)
  (require 'mime-def))

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

rail

SEMI や FLIM などの UA の表示に rail を使っている. ちなみに rail を有効にすると, 以下の様に User-Agent が表示される

(leaf rail
  :init
  (unless (locate-library "rail")
    (el-get-bundle uwabami/rail))
  (setq rail-emulate-genjis t)
  :require t
  )
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-uni)
SEMI の追加設定

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

(leaf mime-setup
  :preface
  (leaf w3m-load)
  (leaf mime-w3m :require t)
  ;; (setq mime-view-text/html-previewer 'shr
  ;;       mime-setup-enable-inline-html 'shr)
  ;; (defvar my:shr-width 72)
  ;; (defun my:shr-insert-document (&rest them)
  ;;   (let ((shr-width my:shr-width)) (apply them)))
  ;; (defun my:mime-shr-preview-text/html (&rest args)
  ;;   (advice-add 'shr-insert-document :around 'my:shr-insert-document)
  ;;   (unwind-protect
  ;;       (apply args)
  ;;     (advice-remove 'shr-insert-document 'my:shr-insert-document)))
  ;; :advice
  ;; (:around mime-shr-preview-text/html
  ;;          my:mime-shr-preview-text/html)
  )

どのアプリケーションで開くか → 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
      (concat my:d:tmp "mime-example"))

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)

個人情報の設定

具体的な設定内容は以下のファイルに置いている

(load (concat my:d:password-store "/wl-info.gpg"))

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

自身のメールアドレスと購読メーリングリストの設定
;; 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/wanderlust/ に集約している local の Mail folder の位置

(setq elmo-maildir-folder-path "~/.cache/wanderlust"
      elmo-localdir-folder-path "~/.cache/wanderlust/local")

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

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

folders の位置の変更

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

Drafts, Trash の置き場所

(setq wl-draft-folder "+Drafts")
(setq wl-trash-folder "+Trash")
(setq elmo-lost+found-folder "+lost+found")
(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)
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)
dispose, delete の設定

Gmail用に%INBOXでは削除を wl-trash-folder への移動ではなく, 「delete」に.

(add-to-list 'wl-dispose-folder-alist
             '("^%INBOX" . remove))

迷惑メール関連も

(add-to-list 'wl-dispose-folder-alist
             '(".*Junk$" . remove))
折り返しの設定

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]"

キーバインド関連

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

(setq wl-user-mail-address-regexp "^uwabami.*\\|^sasakyh.*")
;; 一覧表示での置き換え規則に追加
(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))
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 風、新着レス順にソート. あんまり頑張る気がなかったので el-x にある dflet を使っている。

(defun wl-summary-overview-entity-compare-by-reply-date (a b)
  "Compare message A and B by latest date of replies including thread."
  (dflet ((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)
        ))
;; face の色付け
(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-message-subject-header-contents
 '((t (:foreground "#FF5252" :bold t :italic nil :weight bold ))))
(my:wl-set-face
 'wl-highlight-message-from-header-contents
 '((t (:foreground "#FFD740" :bold t :italic nil :weight bold ))))
(my:wl-set-face
 'wl-highlight-message-date-header-contents
 '((t (:foreground "#5CF19E" :bold t :italic nil :weight bold ))))

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

;; (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 ,my:h:black :5Dbold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-folder-many-face
;;                 '((t (:foreground ,my:h:magenta :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 ,my:n:blue :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 ,my:h:green :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 ,my:h:black :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 ,my:h:yellow :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-forwarded-face
;;                 '((t (:foreground ,my:h:blue :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-high-read-face
;;                 '((t (:foreground ,my:h:green :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-high-unread-face
;;                 '((t (:foreground ,my:h:orange :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 ,my:h:black :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 ,my:h:lightb :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 ,my:n:blue :bold nil :italic nil ))))
;; (my:wl-set-face 'wl-highlight-summary-resend-face
;;                 '((t (:foreground ,my:h:orange :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 ,my:n:violet :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
      '(("Followup-To" . (("From") nil ("Followup-To")))
        ("Newsgroups" . (("From") nil ("Newsgroups")))
        ("Mail-Followup-To" . (("Mail-Followup-To") nil ("Newsgroups")))
        ("From" . (("From") ("To" "Cc") ("Newsgroups")))))

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

(setq wl-default-draft-cite-decorate-author nil)
ドラフトの自動保存の無効化

偶に暴発している様な…? elscreen のせいかしら.

(setq wl-auto-save-drafts-interval 300)
メール本文の文字コード

丸囲み数字なんかが入ってしまうと 勝手にエンコーディングが変わってしまって鬱陶しい. どうしたモンだろうかね.

(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 で扱うために, 以下の関数を定義しておく

(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 'rfc2368))
        (let* ((headers (mapcar (lambda (h) (cons (intern (car h)) (cdr h)))
                                (rfc2368-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;

メールからの予定の取り込み: mhc

(leaf mhc
  :if (file-directory-p (concat (getenv "HOME") "/.config/mhc"))
  :ensure t
  :disabled t
  :commands (mhc-import)
  :init
  (setq mhc-calendar-day-strings ["日" "月" "火" "水" "木" "金" "土"]
        mhc-calendar-header-function 'mhc-calendar-make-header-ja
        mhc-calendar-language 'japanese)
  )

閉じタグの入力補助: smartparens

(leaf smartparens
  :disabled t
  :ensure t
  :blackout t
  :defun (sp-pair)
  :hook (after-init-hook . smartparens-global-mode)
  :config
  (require 'smartparens-config)
  (sp-pair "=" "=" :actions '(wrap))
  (sp-pair "+" "+" :actions '(wrap))
  (sp-pair "<" ">" :actions '(wrap))
  (sp-pair "$" "$" :actions '(wrap))
  )

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

#RRGGBB のカラーコードに勝手に色が付く.CSS の編集中なんかで地味に便利.

(leaf rainbow-mode
  :ensure t
  :blackout `((rainbow-mode . ,(format " %s" "\x1F308")))
  )

対応する括弧を見易く: rainbow-delimiters

対応する括弧を強調表示してくれる RainbowDelimiters: global に有効にするのは他の Major modeとの衝突があるので止めた方が良い, らしい.

(leaf rainbow-delimiters
  :ensure t
  :blackout t
  :hook (prog-mode-hook . rainbow-delimiters-mode)
  )

Org-mode

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

基本設定: org

目新しい設定はしていない, と思う.

;; org-plus-contrib
(leaf org
  :ensure t
  :blackout `((org-mode . ,(all-the-icons-icon-for-mode 'org-mode)))
  :bind (("C-x n s" . org-narrow-to-subtree)
         ("C-x n w" . widen)
         ("C-c a"   . org-agenda)
         ;; ("C-x m"   . org-capture)
         ;; ("C-x M"   . org-journal-new-entry)
         )
  :advice
  (:before org-calendar-holiday
           (lambda () (require 'japanese-holidays)))
  :mode
  ;; 昔のメモ(howm)も org-mode で開く
  (("\\.org\\'" "\\.howm\\'". org-mode))
  :preface
  ;;; timestamp 更新文字列の変更:
  ;;  org-mode では #+DATE: をひっかける用に(#は小文字).
  (defun my:org-timestamp-hook ()
    "Change `time-stamp-start' in org-mode"
    (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)
          (leaf org-tempo :require t)))
    (org-todo-statistics-hook       . my:org-archive-done-tasks)
    (org-todo-after-statistics-hook . my:org-archive-done-tasks))
  :custom
  `(;; Nextcloud に保存する
    (org-directory              . ,(expand-file-name my:d:org))
    ;; return でリンクを辿る
    (org-return-follows-link    . t)
    ;; 見出しを畳んで表示
    (org-startup-folded         . t)
    ;; 折り返し無し
    (org-startup-truncated      . 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)")
                                    (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)
                                    ))
    ;; DONE → Archive.org に移動
    (org-archive-location       . "Archive.org::")
    ;; modules → とりあえずクリアしておく
    (org-modules  . '())
    )
  :config
  ;; +打ち消し+ の font-lock の変更 →これはテーマに任せるべき?
  (setq org-emphasis-alist
        (cons '("+" '(:strike-through t :foreground "#999999"))
              (cl-delete "+" org-emphasis-alist :key 'car :test 'equal)))
  )

Org-Id

(leaf org-id
  :commands
  (my:add-custom-id
   my:get-custom-id
   my:org-custom-id-get
   my:delete-all-id-in-file
   my:org-id-add-to-headlines-in-file
   )
  :init
  (leaf org-macs :require t)
  :custom
  `((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-id-link-to-org-use-id . create-if-interactive-and-no-custom-id)
    )
  :config
  (defun my:add-custom-id ()
    "Add \"CUSTOM_ID\" to the current tree if not assigned yet."
    (interactive)
    (my:org-custom-id-get nil t))
  ;;
  (defun my:get-custom-id ()
    "Return a part of UUID with an \"org\" prefix.
e.g. \"org3ca6ef0c\"."
    (let* ((id (org-id-new "")))
      (when (org-uuidgen-p id)
        (downcase (concat "org"  (substring (org-id-new "") 0 8))))))
  ;;
  (defun my:org-custom-id-get (&optional pom create)
    "Get the CUSTOM_ID property of the entry at point-or-marker POM.
See https://writequit.org/articles/emacs-org-mode-generate-ids.html"
    (interactive)
    (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: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)))))
  ;;
  (defun my: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."))
  ;;
  (defun my:org-id-add-to-headlines-in-file ()
    "Add CUSTOM_ID properties to all headlines in the current file.
See 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))))
  )

Babel

(leaf org-babel
  :after all-the-icons-in-terminal
  :blackout
  `((org-src-mode . ,(format " %s" (all-the-icons-octicon "code"))))
  :custom
  (;; font-lock
   (org-src-fontify-natively         . t)
   ;; TAB の挙動
   (org-src-tab-acts-natively        . t)
   ;; インデント
   (org-edit-src-content-indentation . 0)
   ;; インデントを残す
   (org-src-preserve-indentation     . t))
  )

Org-capture: メモ取り

キーバインドは以前 changelog memo をやっていた時の癖で C-x m をメモにしている. → capture は org-roam に丸投げすることにした.

(leaf org-capture
  :if (file-directory-p my:d:org)
  :commands org-capture
  ;; :pl-setq
  ;; (my:org:calendar1 my:org:calendar2)   ;; 名前がイケてないっ!
  :init (leaf doct :ensure t) ;; capture template を上手く書く拡張
  :config
  (setq org-default-notes-file (expand-file-name "INBOX.org" my:d:org))
  (setq org-capture-templates
        (doct '(;;
                ("Org-roam Memo"
                 :keys "m"
                 :type plain
                 :template "* MEMO <%<%Y-%m-%d %H:%M>> %^{title}\n  %?"
                 :target
                 :prepend nil
                 :unnarrowed nil
                 :kill-buffer t
                 ;; :function org-roam-dailies-capture-today
                 )
                ("Schedule: 個人スケジュール"
                 :keys "s"
                 :type plain
                 :file (lambda() (expand-file-name "Schedule.org" my:d:org))
                 :template "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar1)\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
                 :prepend nil
                 :unnarrowed nil
                 :kill-buffer t
                 )
                ("Univ. Schedule: 大学関連"
                 :keys "u"
                 :type plain
                 :file (lambda() (expand-file-name "Univ.org" my:d:org))
                 :template "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar2)\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
                 :prepend nil
                 :unnarrowed nil
                 :kill-buffer t
                 )
                ))
        )
  )

Org-roam: リンク付きメモ

(leaf org-roam
  :if (and (file-directory-p my:d:org)
           (and (executable-find "rg")
                (executable-find "sqlite3")))
  :pl-setq
  (my:org:calendar1 my:org:calendar2)   ;; 名前がイケてないっ!
  :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 o" . org-id-get-create)
   ("C-c n t" . org-roam-tag-add)
   ("C-c n l" . org-roam-buffer-toggle)
   ("C-x m"   . org-roam-dailies-capture-today)
   )
  :ensure t
  :init (setq org-roam-v2-ack t)
  :custom
  `(;; (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))
    (org-roam-mode-section-functions
     . (list #'org-roam-backlinks-section
             #'org-roam-reflinks-section
            ;; #'org-roam-unlinked-references-section
            ))
    ;; (org-roam-db-update-on-save . t)
    (org-roam-dailies-directory . "") ;; relative path→flatten!
    (org-roam-dailies-capture-templates
     . '(
         ("m" "memo" entry "* MEMO <%<%Y-%m-%d %H:%M>> %^{title}\n  %?"
          :target (file+head "%<%Y-%m-%d>.org"
                             "#+title: %<%Y年%m月%d日(%a)>")
          :prepend nil
          :kill-buffer t
          )
         ("t" "ToDo" entry "* ToDo <%<%Y-%m-%d %H:%M>> %^{title}\n  %?"
          :target (file+head "%<%Y-%m-%d>.org"
                             "#+title: %<%Y年%m月%d日(%a)>")
          :prepend nil
          :kill-buffer t
          )
         ("s" "Schedule: 個人スケジュール" entry
          "* %^{title}\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:"
          :target (file ,(expand-file-name "Schedule.org" my:d:org))
          :prepend t   ;; prepend nil としたいが, 末尾まで行ってくれないので諦める.
          :kill-buffer t
          )
         ("u" "Univ: 大学関連 スケジュール" entry
          "* %^{title}\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:"
          :target (file ,(expand-file-name "Univ.org" my:d:org))
          :prepend t   ;; prepend nil としたいが, 末尾まで行ってくれないので諦める.
          :kill-buffer t
          )
         ))
     )
  :defer-config
  (org-roam-db-autosync-enable)
  )

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

GTD 用の設定.後述の org-gcalorgmine で取得したデータも表示している. ついでに

  • 土曜日をの face を追加.
  • 祝日, 休日を日曜と同じfaceにする.

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

(leaf org-agenda
  :if (file-directory-p my:d:org)
  :defer-config
  (defface my:org-agenda-date-saturday
    '((t (:foreground "#7FBFFF" :bold t )))
    "Agenda 表示中の土曜日用のface")
  (defface my:org-agenda-date-today-saturday
    '((t (:inherit my:org-agenda-date-saturday :underline t)))
    "Agenda 表示中の今日かつ土曜日用のface")
  (defface my:org-agenda-date-today-weekend
    '((t (:inherit org-agenda-date-weekend :underline t)))
    "Agenda 表示中の今日かつ日・祝日用のface")
  ;; こっからは 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-twitter
    '((t (:foreground "#CCCCCC")))
    "Agenda 表示中, Twitter log の表示 face"
    :group 'org-agenda )
  ;; 更新用の関数 - とりあえず動いているので良しとするが,
  ;; リファクタリングしたい
  (defun my:org-agenda-day-face-function (date)
    "Compute DATE face for saturday, holidays."
    (cl-dolist (file (org-agenda-files nil 'ifmode))
      (cond
       ((member (calendar-day-of-week date) '(0))
        (if (org-agenda-todayp date)
            (cl-return 'my:org-agenda-date-today-weekend))
        (cl-return 'org-agenda-date-weekend))
       ((member (calendar-day-of-week date) '(6))
        (if (org-agenda-todayp date)
            (cl-return 'my:org-agenda-date-today-saturday))
        (cl-return 'my:org-agenda-date-saturday)))
      (let ((face
             (cl-dolist (entry (org-agenda-get-day-entries file date))
               (let ((category (with-temp-buffer
                                 (insert entry)
                                 (org-get-category (point-min)))))
                 (when (or (string= "祝日" category)
                           (string= "休日" category))
                   (if (org-agenda-todayp date)
                       (cl-return 'my:org-agenda-date-today-weekend)
                     (cl-return 'org-agenda-date-weekend)))))))
        (when face (cl-return face)))))
  (setq org-agenda-day-face-function
        'my:org-agenda-day-face-function)
  ;; font-lock の適用. loop減らせないかなぁ….
  (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) (point-at-eol)
                             '(face my:org-agenda-calendar-Univ)))
      (goto-char (point-min))
      (while (re-search-forward "Schedule:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face my:org-agenda-calendar-Schedule)))
      (goto-char (point-min))
      (while (re-search-forward "DebianJP:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face my:org-agenda-calendar-DebianJP)))
      (goto-char (point-min))
      (while (re-search-forward "GFD:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face my:org-agenda-calendar-GFD)))
      (goto-char (point-min))
      (while (re-search-forward "twitter:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face my:org-agenda-calendar-twitter)))
      (goto-char (point-min))
      (while (re-search-forward "祝日:\\|休日:\\|誕生日:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face org-agenda-date-weekend)))
      ))
  ;;
  ;;
  (add-hook 'org-agenda-finalize-hook
            #'my:org-agenda-finalize-font-lock)
  ;;
  (setq org-agenda-span 'day
        org-agenda-format-date "%Y/%m/%d (%a)"
        org-agenda-start-on-weekday 0
        org-agenda-weekend-days '(0)
        org-agenda-repeating-timestampo-show-all t
        org-agenda-day-face-function 'my:org-agenda-day-face-function
        org-agenda-custom-commands
        '(
          ("n" "agenda and all TODO list"
           (
            (agenda ""
                    ((org-agenda-ndays 1)
                     (org-agenda-entry-types '(:timestamp :sexp))))
            (todo "TODO"
                  ((org-agenda-prefix-format " %i %-22:c")))
            (todo "新規|着手|進行中|確認"
                  ((org-agenda-prefix-format " %i %-22:c")))
            (todo "WAIT"
                  ((org-agenda-prefix-format " %i %-22:c")))

            (todo "SOMEDAY"
                  ((org-agenda-prefix-format " %i %-22:c")))
            ))
          ("N" "All memo entry"
           (;;
            (todo "MEMO")
            ))
          )
        )
  (dolist (file
           '(;; "Archive.org"   <-- 🤔
             ;; Calendar
             "Holidays.org"
             "Schedule.org"
             "GFD.org"
             "Univ.org"
             "DebianJP.org"
             ;; INBOX
             "INBOX.org"
             ;; misc
             "twitter.org"
             ;; Project
             "redmine_GFD.org"
             ;; "redmine_FluidSoc.org"
             ))
    (add-to-list 'org-agenda-files (expand-file-name file my:d:org)))
  (add-to-list 'org-agenda-files
               (locate-user-emacs-file "README.org"))
  ;; (add-to-list 'org-agenda-files
  ;;             (expand-file-name "Public/web.org" (getenv "HOME")))
  ;; (add-to-list 'org-agenda-files
  ;;              (expand-file-name "Public/404.org" (getenv "HOME")))
  ;; org-wiki
  ;; (dolist (file
  ;;          (directory-files-recursively
  ;;           (expand-file-name "Public/cc-env" (getenv "HOME")) "org$"))
  ;;   (add-to-list 'org-agenda-files file))
  ;; github repos.
  ;; (dolist (file
  ;;          (directory-files-recursively
  ;;           (expand-file-name "Public/software" (getenv "HOME")) "org$"))
  ;;   (add-to-list 'org-agenda-files file))
  ;;
)

Org-roam と Org-agenda

org-roam で db に query 投げて, org-agenda-files を更新するように. 元ネタ: Task management with org-roam Vol. 5: Dynamic and fast agenda

(leaf vulpea
  :if (and (file-directory-p my:d:org)
           (and (executable-find "rg")
                (executable-find "sqlite3")))
  :after org-agenda
  :el-get  d12frosted/vulpea
  :advice
  (:before org-agenda
           my:vulpea-agenda-files-update)
  :hook
  ((org-roam-db-autosync-mode . vulpea-db-autosync-enable)
   (find-file-hook            . vulpea-project-update-tag)
   (before-save-hook          . vulpea-project-update-tag))
  :config
  (defun vulpea-project-p ()
    "Return non-nil if current buffer has any todo entry.

TODO entries marked as done are ignored, meaning the this
function returns nil if current buffer contains only completed
tasks."
    (seq-find                                 ; (3)
     (lambda (type)
       (eq type 'todo))
     (org-element-map                         ; (2)
         (org-element-parse-buffer 'headline) ; (1)
         'headline
       (lambda (h)
         (org-element-property :todo-type h)))))

  (defun vulpea-project-update-tag ()
    "Update PROJECT tag in the current buffer."
    (when (and (not (active-minibuffer-window))
               (vulpea-buffer-p))
      (save-excursion
        (goto-char (point-min))
        (let* ((tags (vulpea-buffer-tags-get))
               (original-tags tags))
          (if (vulpea-project-p)
              (setq tags (cons "project" tags))
            (setq tags (remove "project" tags)))

          ;; cleanup duplicates
          (setq tags (seq-uniq tags))

          ;; update tags if changed
          (when (or (seq-difference tags original-tags)
                    (seq-difference original-tags tags))
            (apply #'vulpea-buffer-tags-set tags))))))

  (defun vulpea-buffer-p ()
    "Return non-nil if the currently visited buffer is a note."
    (and buffer-file-name
         (string-prefix-p
          (expand-file-name (file-name-as-directory org-roam-directory))
          (file-name-directory buffer-file-name))))

  (defun vulpea-project-files ()
    "Return a list of note files containing 'project' tag." ;
    (seq-uniq
     (seq-map
      #'car
      (org-roam-db-query
       [:select [nodes:file]
                :from tags
                :left-join nodes
                :on (= tags:node-id nodes:id)
                :where (like tag (quote "%\"project\"%"))]))))

  (defun my:vulpea-agenda-files-update (&rest _)
    "Update/Add the value of `org-agenda-files'."
    (if (vulpea-project-files)
        (progn
          (dolist (file (vulpea-project-files))
            (add-to-list 'org-agenda-files file))
          (setq org-agenada-files (delete-dups org-agenda-files)))))
  )

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")))
  :ensure t
  :commands (org-gcal-fetch org-gcal-sync)
  :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 org-generic-id
    :custom
    `((org-generic-id-locations-file
       . ,(expand-file-name "org-generic-id-locations"  my:d:tmp))
      )
    )
  (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))
    :custom
    `((request-storage-directory . ,(expand-file-name "request" my:d:tmp)))
    )
  (leaf persist
    :config
    (setq persist--directory-location (expand-file-name "persist" my:d:tmp)))
  :custom
  `((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)
    ;;
    (alert-log-messages    . t)
    (alert-default-style   .'libnotify))
  :pl-setq
  (org-gcal-client-id
   org-gcal-client-secret
   org-gcal-file-alist)
  ;; :hook (org-capture-after-finalize-hook  . org-gcal-fetch)
  )

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
    :commands (orgmine-mode)
    :el-get kametoku/orgmine
    :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"
          orgmine-default-todo-keyword "新規")
    )
  )

Org-Wiki

(leaf org-wiki
  :if (file-directory-p "~/Public/cc-env")
  :el-get uwabami/org-wiki
  :custom
  `((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 (point-at-bol))))
  :hook
  ;; ((org-export-before-processing-hook . my:remove-org-newlines-at-cjk-text))
  :custom
  ((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 して, それを処理する様にしています.

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

(leaf ox-html
  :after ox
  :init
  (leaf ox-jekyll
    :if (file-directory-p "~/Public/cc-env")
    :el-get uwabami/ox-jekyll)
  (leaf s :ensure t)
  :custom
  ((org-html-table-align-individual-fields . nil)
   (org-html-table-default-attributes      . nil)
   (org-html-html5-fancy                   . t)
   (org-html-doctype                       . "html5")
   ;; (org-html-container-element             . "div")
   (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"
    (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"
    (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"
    (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"
    (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"
    (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"
    (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

(leaf ox-latex
  :after ox
  :preface
  :init
  (leaf ox-beamer
    :custom
    `((org-beamer-frame-level . 2)
      (org-beamer-frame-default-options . "fragile,squeeze,c")
      (org-latex-compiler . "latexmk -pvc")
      )
    :config
    ;; for Beamer
    (add-to-list 'org-export-options-alist
                 '(:shortdate    "SHORTDATE"   nil nil))
    (add-to-list 'org-export-options-alist
                 '(:shorttitle   "SHORTTITLE"  nil nil))
    (add-to-list 'org-export-options-alist
                 '(:shortauthor  "SHORTAUTHOR" nil nil))
    (add-to-list 'org-export-options-alist
                 '(:institute    "INSTITUTE"   nil nil))
    )
  :custom
  `((org-latex-default-class . "my:uplatex")
    (org-latex-pdf-process   . '("latexmk -pvc %f"))
    (org-latex-hyperref-template
     .
     "\\hypersetup{
pdfauthor={%a},
pdftitle={%t},
pdfkeywords={%k},
pdfsubject={%d},
pdfcreator={%c},
pdflang={%L},
colorlinks,
}")
    (org-latex-classes
     . '(("my:uplatex"
          "\\documentclass[a4j,uplatex]{jsarticle}
            [NO-DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
          ("\\section\{%s\}" . "\\section*\{%s\}")
          ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
          ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))
         ("my:jlreq"
          "\\documentclass{jlreq}
            [NO-DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
          ("\\section\{%s\}" . "\\section*\{%s\}")
          ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
          ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))
         ("my:beamer"
          "\\documentclass[dvipdfmx,presentation]{beamer}
             [NO-DEFAULT-PACKAGES] [NO-PACKAGES] [EXTRA]"
          ("\\section\{%s\}" . "\\section*\{%s\}")
          ("\\subsection\{%s\}" . "\\subsection*\{%s\}")
          ("\\subsubsection\{%s\}" . "\\subsubsection*\{%s\}"))))
    )
  )

Jekyll

Publish

(leaf ox-publish
  :if (file-directory-p "~/Public/cc-env")
  :after (ox-jekyll org-wiki)
  :commands my:org-wiki-publish
  :custom
  `((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))
  )

Org-ref: 文献参照, 引用など

(leaf org-ref
  :if (file-directory-p my:d:nextcloud)
  :disabled t
  :ensure t
  :custom
  `((org-ref-bibliography-notes   . ,(expand-file-name "INBOX.org" my:d:org))
    (org-ref-default-bibliography . ,(expand-file-name "references.bib" my:d:nextcloud))
    (org-ref-pdf-directory        . ,(expand-file-name "Papers" my:d:nextcloud))
    )
  )

Outline Magic

どんどん増えそう.

(leaf outline
  :init
  (leaf outline-magic
    :ensure t
    :bind ((:outline-minor-mode-map
            ("C-c TAB" . outline-cycle)))
    :hook ((LaTeX-mode-hook . my:add-outline-headings)
           (LaTeX-mode-hook . outline-minor-mode))
    :init
    (defun my:add-outline-headings ()
      "Custom: Add promotion headings"
      (setq outline-promotion-headings '("\\chapter"
                                         "\\section"
                                         "\\subsection"
                                         "\\subsubsection"
                                         "\\paragraph"
                                         "\\subparagraph"
                                         "\\begin{frame}"
                                         )))
    )
  )

VCS

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

git{attributes,config,ignore}-mode, git-commit

(leaf *git
  :init
  (leaf git-commit :ensure t)
  (leaf gitattributes-mode :ensure t)
  (leaf gitconfig-mode :ensure t)
  (leaf gitignore-mode :ensure t)
  )

magit:

magit は Emacs の Git Frontend. 結局の所 CUI でコマンド叩く事も多いけれど, これはこれで重宝している.

(leaf magit
  :bind (("C-x g" . magit-status))
  :ensure t
  :init
  (leaf transient
    :custom
    `((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))
    )
  :config
  (setq magit-completing-read-function 'ido-completing-read
        magit-refs-show-commit-count   'all
        magit-log-buffer-file-locked   t
        magit-revision-show-gravatars  nil
        )
  )

Forge

Gitub/Gitlab と Magit の連携

(leaf forge
  :disabled t
  :after magit
  )

Git Gutter+

飽きたら止めるかもしれないけれど.

(leaf git-gutter+
  :ensure t
  :blackout `((git-gutter+-mode
               . ,(format "%s" (all-the-icons-octicon "git-merge"))))
  :bind ("C-x G" . global-git-gutter+-mode)
  )

関数定義を辿る: dump-jump, smart-jump

あまり上手く使えていない.

(leaf dumb-jump
  :disabled t
  :if (executable-find "rg")
  :ensure t
  :bind (("M-g o" . dumb-jump-go-other-window)
         ("M-g j" . dumb-jump-go)
         ("M-g i" . dumb-jump-go-prompt)
         ("M-g x" . dumb-jump-go-prefer-external)
         ("M-g z" . dumb-jump-go-prefer-external-other-window))
  :custom '((dumb-jump-selector       . 'ido)
            (dumb-jump-force-searcher . 'rg))
  )
(leaf smart-jump
  :disabled t
  :ensure t
  :config
  (smart-jump-setup-default-registers)
  )

言語毎の補完: company-mode

まだうまく使いこなせていない.

を有効にしてみたが, これで良いのかしら…?

(leaf company
  :disabled t
  :ensure t
  :blackout t
  :custom
  ((company-require-match             . 'never)
   (company-idle-delay                . 0)
   (company-selection-wrap-around     . t)
   (company-tooltip-align-annotations . t)
   (company-minimum-prefix-length     . 4)
   ;; disable some noisy default backends
   (company-backends . '(company-files
                         company-capf
                         (company-dabbrev-code
                          company-gtags
                          company-etags
                          company-keywords))))
  :bind
  (;; ("<tab>"    . company-indent-or-complete-common)
   (:company-active-map
    ("C-n"     . company-select-next)
    ("C-p"     . company-select-previous)
    ("C-s"     . company-filter-candidates)
    ("C-<tab>" . company-complete-common-or-cycle)
    ("<tab>"   . company-indent-or-complete-common)
    )
   (:company-search-map
    ("C-p" . company-select-previous)
    ("C-n" . company-select-next)))
  :hook
  `((after-init-hook      . global-company-mode)
    (minibuffer-setup-hook . ,(lambda ()
                                (company-mode -1)))
    )
  )
;; (leaf company-statistics
;;   :disabled t
;;   :ensure t
;;   :custom
;;   `((company-statistics-size . 2000)
;;     (company-statistics-file
;;      . ,(expand-file-name "company-statistics-cache.el" my:d:tmp))
;;     (company-transformers
;;      . (company-sort-by-statistics company-sort-by-backed-importance)))
;;   :config
;;   (company-statistics-mode)
;;   )
  ;; :init
  ;; (defun edit-category-table-for-company-dabbrev (&optional table)
  ;;   (define-category ?s "word constituents for company-dabbrev" table)
  ;;   (let ((i 0))
  ;;     (while (< i 128)
  ;;       (if (equal ?w (char-syntax i))
  ;;           (modify-category-entry i ?s table)
  ;;         (modify-category-entry i ?s table t))
  ;;       (setq i (1+ i)))))
  ;; (edit-category-table-for-company-dabbrev)
  ;; ;; (add-hook 'TeX-mode-hook 'edit-category-table-for-company-dabbrev) ; 下の追記参照
  ;; (setq company-dabbrev-char-regexp "\\cs")

SOMEDAY テンプレート補完: yasnippet [0/1]

(leaf yasnippet
  :ensure t
  :blackout t
  :disabled t
  :custom
  `((yas-indent-line  . 'fixed)
    (yas-snippet-dirs . '(,(expand-file-name "snippets" my:d:share))))
  :hook (after-init-hook . yas-global-mode)
  :bind ((:yas-keymap
          ("C-<tab>" . nil)
          ("<tab>"   . nil))  ; for company
         (:yas-minor-mode-map
          ("<tab>"   . nil)
          ("C-<tab>" . nil)
          ("C-c y e" . yas-expand)
          ("C-c y i" . yas-insert-snippet)
          ("C-c y n" . yas-new-snippet)
          ("C-c y v" . yas-visit-snippet-file)
          ("C-c y l" . yas-describe-tables)
          ("C-c y g" . yas-reload-all)))
  :init
  (leaf yasnippet-snippets :ensure t)
  (leaf yatemplate :ensure t)
  )
  • [ ] 意図しない所で発火してテンプレートが挿入されてしまうので保留中

SOMEDAY flymake: on-the-fly check [0/1]

on-the-fly syntax checker. 同様の拡張には flycheck があるけれど, flycheck はいろいろやりすぎてて私には合わない.

(leaf flymake
  :ensure t
  )
  • [ ] 設定調整が必要

LSP: lsp-mode

(leaf lsp-mode
  :disabled t
  :if (executable-find "fortls")
  :ensure t
  :custom
  `((lsp-print-io             . nil)
    (lsp-trace                . nil)
    (lsp-print-performance    . nil)
    (lsp-auto-guess-root      . t)
    (lsp-document-sync-method . 'incremental)
    (lsp-response-timeout     . 5)
    (lsp-prefer-flymake       .'flymake)
    (lsp-session-file         .,(expand-file-name "lsp-session-v1" my:d:tmp))
   )
  :init
  (leaf lsp-ui
    :ensure t
    :custom
    ((lsp-ui-doc-enable            . t)
     (lsp-ui-doc-header            . t)
     (lsp-ui-doc-include-signature . t)
     (lsp-ui-doc-position          . 'at-point)
     (lsp-ui-doc-max-width         . 150)
     (lsp-ui-doc-max-height        . 30)
     (lsp-ui-doc-use-childframe    . nil)
     (lsp-ui-doc-use-webkit        . nil)
     (lsp-ui-flycheck-enable       . nil)
     (lsp-ui-peek-enable           . t)
     (lsp-ui-peek-peek-height      . 20)
     (lsp-ui-peek-list-width       . 50)
     (lsp-ui-peek-fontify          . 'on-demand) ;; never, on-demand, or always
     )
    )
  :hook
  ((lsp-mode-hook . lsp-ui-mode)
   (f90-mode-hook . lsp))
  )

TeX: AUCTeX

やっている事は

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

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

(leaf auctex
  :if (and (executable-find "uplatex")
           (executable-find "latexmk"))
  :load-path "/usr/share/emacs/site-lisp/auctex"
  :init
  (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)))
  (leaf reftex
    :custom ((reftex-plug-into-AUCTeX               . t)
             (reftex-cite-prompt-optional-args      . t)
             (reftex-toc-split-windows-horizontally . t)
             )
    )
  (leaf auctex-latexmk
    :el-get tom-tan/auctex-latexmk
    :custom
    `((auctex-latexmk-inherit-TeX-PDF-mode . t))
    :config
    (setq TeX-command-output-list '(("LaTeXMk" ("pdf")))
          TeX-command-default "LaTexMk"
          japanese-LaTeX-command-default "LaTeX")
    )
  (load "auctex" t t)
  (load "preview-latex" t t)
  :hook
  (LaTeX-mode-hook . (lambda ()
                       (turn-on-reftex)
                       (auctex-latexmk-setup)
                       (setq-default TeX-command-default "LaTexMk"
                                     japanese-LaTeX-command-default "LaTeX")
                       (TeX-PDF-mode)
                       (TeX-source-correlate-mode)
                       (LaTeX-math-mode)
                       (outline-minor-mode)
                       ))
  :custom
  `((TeX-auto-local
     . ,(expand-file-name "auctex/auto" my:d:tmp))
    (TeX-style-local
     . ,(expand-file-name "auctex/style" my:d:tmp))
    (TeX-default-mode                  . 'japanese-latex-mode) ;; 🤔
    (japanese-TeX-engine-default       . 'uptex)
    (japanese-LaTeX-default-style      . "jlreq") ;; jsarticle? bxjsarticle?
    (TeX-engine                        . 'uptex)
    (TeX-PDF-from-DVI                  . "Dvipdfmx")
    (TeX-view-program-selection        . '((output-dvi "xdvi")
                                           (output-pdf "Evince")
                                           (output-html "xdg-open")))
    (LaTeX-figure-label                . "fig:")
    (LaTeX-table-label                 . "tab:")
    (LaTeX-section-label               . "sec:")
    (TeX-command-default               . "LaTexMk")
    (TeX-parse-self                    . t)
    (TeX-auto-save                     . t)
    (TeX-auto-untabify                 . t)
    (TeX-source-correlate-mode         . t)
    (TeX-source-correlate-start-server . t)
    (TeX-source-correlate-method       . 'synctex)
    (font-latex-fontify-script         . nil)
    (font-latex-script-display         . nil)
    (font-latex-fontify-sectioning     . 1.0)
    )
  )

Autoconf

いれてみたけれど, はてさて.

(leaf sh-autoconf
  :el-get (sh-autoconf
           :type http
           :url "https://download.tuxfamily.org/user42/sh-autoconf.el")
  :mode (("/configure\\.\\(ac\\|in\\)\\'"    . sh-mode)
         ("/ac\\(include\\|local\\)\\.m4\\'" . sh-mode))
  )

Fortran

(leaf f90
  :mode ("\\.\\(f|F\\)\\(90|95|03|08\\)$" . f90-mode)
  :init
  (leaf f90-indent
    :custom
    ((f90-do-indent               . 3) ;; 2? 4?
     (f90-if-indent               . 3) ;; 2? 4?
     (f90-type-indent             . 3) ;; 2? 4?
     (f90-program-indent          . 3) ;; 2? 4?
     (f90-continuation-indent     . 3) ;; 2? 4?
     (f90-directive-comment-re    . "!omp\\$" )
     (f90-indented-comment-re     . "!" )
     (f90-break-delimiters        . "[-+\\*/><=,% \t]")
     (f90-break-before-delimiters . t)
     (f90-beginning-ampersand     . nil)
     (f90-smart-end               . 'blink)
     (f90-auto-keyword-case       . nil)
     (f90-leave-line-no           . nil)
     (f90-comment-region          . "!! ")
     (f90-indent-comment          . "! "))
    )
  )

Markdown

markdown自体はあまり好きじゃないんだけれど, 必要に迫られて書く事が増えてきたので設定しておく.

(leaf markdown-mode
  :if (executable-find "pandoc")
  :mode ("\\.\\(md\\|markdown\\|mkd\\)\\'" . gfm-mode)
  :preface
  (defun my:disable-electric-indent-local-mode ()
    (electric-indent-local-mode -1))
  :hook
  `((markdown-mode-hook . my:disable-electric-indent-local-mode)
    (gfm-mode-hook      . my:disable-electric-indent-local-mode))
  :custom
  `((markdown-command
     . "pandoc --from markdown_github -t html5 --mathjax --highlight-style pygments"))
  )

SCSS

ちょいちょい弄る機会が増えてきたので導入.

(leaf scss-mode
  :if (executable-find "sass")
  :ensure t
  :mode "\\.scss\\'"
  :custom
  `((scss-sass-command . ,(executable-find "sass")))
  )

PlantUML

(leaf plantuml-mode
  :if (executable-find "plantuml")
  :ensure t
  :custom
  `((plantuml-executable-path . ,(executable-find "plantuml"))
    (plantuml-default-exec-mode . 'executable))
  :init
;;   (add-to-list
;;    'org-src-lang-modes '("plantuml" . plantuml))
  )

Re:VIEW

(leaf review-mode
  :ensure t
  :custom
  `((review-mode-use-skk-mode . t)
    (reivew-use-em            . t)
    )
  )

その他のモード設定

読み込むだけの mode の設定. 設定が増えたら別途まとめる。

(leaf *misc-mode
  :init
  (leaf flycheck
    :disabled t
    :ensure t
    :config
    (flycheck-define-checker
     textlint
     "A linter for prose."
     :command ("textlint" "--format" "unix"
               ;; "--rule" "no-mix-dearu-desumasu" "--rule" "max-ten" "--rule" "spellcheck-tech-word"
               source-inplace)
     :error-patterns
     ((warning line-start (file-name) ":" line ":" column ": "
               (id (one-or-more (not (any " "))))
               (message (one-or-more not-newline)
                        (zero-or-more "\n" (any " ") (one-or-more not-newline)))
               line-end))
     :modes (text-mode markdown-mode gfm-mode wl-draft-mode))
    (add-to-list 'flycheck-checkers 'textlint)
    )
  (leaf debian-el
    :custom
    `((debian-bug-download-directory . "~/Downloads"))
    )
  (leaf rd-mode
    :mode "\\.rd$"
    :hook
    (rd-mode-hook . rd-show-other-block-all))
  (leaf yaml-mode
    :ensure t
    :mode "\\(\.yml\\|\.yaml\\)"
    )
  (leaf generic-x)
  (leaf textile-mode :ensure t)
  (leaf lua-mode :ensure t)
  (leaf debian-el)
  (leaf dpkg-dev-el)
  (leaf sh-mode
    :custom ((system-uses-terminfo . nil))
    )
  (leaf apt-sources-list :ensure t
    :custom
    ((apt-sources-list-suites
      . '("stable" "stable-backports"
          "testing" "testing-backports"
          "unstable" "experimental"
          "jessie" "jessie-backports"
          "stretch" "stretch-backports"
          "buster" "buster-backports"
          "bullseye"
          "sid")))
    )
  (leaf woman
    ;; :custom-face
    ;; ((woman-bold   . '((t (:inherit font-lock-type-face :bold t))))
    ;; (woman-italic . '((t (:inherit font-lock-keyword-face :underline t)))))
    )
  (leaf info-colors
    :ensure t
    :hook
    (Info-selection #'info-colors-fontify-node))
  )

SSH config mode

(leaf ssh-config-mode
  :ensure t
  :mode ((("/\\.ssh/config\\'" "/sshd?_config\\'") . ssh-config-mode)
         ("/known_hosts\\'"                        . ssh-known-hosts-mode)
         ("/authorized_keys?\\'"                   . ssh-authorized-keys-mode))
  :hook (ssh-config-mode . turn-on-font-lock)
  )

SOMEDAY 日記: tDiary [0/1]

(leaf tdiary-mode
  :if (and my:d:password-store
           (file-directory-p (concat (getenv "HOME") "/Nextcloud/tdiary")))
  :commands (tdiary-mode tdiary-replace tdiary-append)
  :el-get uwabami/tdiary-mode
  :defvar tdiary-passwd-file
  :pl-setq
  (tdiary-csrf-key tdiary-passwd-file)
  ;; :init
  ;; (setq tdiary-text-save-p t)
  :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)
  ;; (with-eval-after-load 'tdiary-mode
  ;;   (setq tdiary-text-save-p t))
  )
  • [ ] org2blog で tDiary を更新できないか妄想している

テーマ, フォント, モードライン, などなど

SOMEDAY フォント [0/1]

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

;;;###autoload
(defun my:load-window-config ()
  "load window-system specific settings"
  (interactive)
  (progn
    (set-face-attribute 'default nil
                        :family "FSMRMP"
                        :height 220)
    (set-face-attribute 'fixed-pitch nil
                        :family "FSMRMP"
                        :height 220)
    (set-face-attribute 'variable-pitch nil
                        :family "FSMRMP"
                        :height 220)
    ;; Japanese
    (set-fontset-font nil
                      'japanese-jisx0213.2004-1
                      (font-spec :family "AGVRMP" :height 220))
    (set-fontset-font nil
                      'japanese-jisx0213-2
                      (font-spec :family "AGVRMP" :height 220))
    (set-fontset-font nil
                      'katakana-jisx0201
                      (font-spec :family "AGVRMP" :height 220))
    ;; Latin with pronounciation annotations
    (set-fontset-font nil
                      '(#x0080 . #x024F)
                      (font-spec :family "AGVRMP" :height 220))
    ;; Math symbols
    (set-fontset-font nil
                      '(#x2200 . #x22FF)
                      (font-spec :family "AGVRMP" :height 220))
    ;; Greek
    (set-fontset-font nil
                      '(#x0370 . #x03FF)
                      (font-spec :family "AGVRMP" :height 220))
    ;; Some Icons
    (set-fontset-font nil
                      '(#xE0A0 . #xEEE0)
                      (font-spec :family "AGVRMP" :height 220))
    ))
;;;###autoload
(defun my:load-side-window-config ()
  "load window-system specific settings"
  (interactive)
  (progn
    (set-face-attribute 'default nil
                        :family "AGVRMP"
                        :height 180)
    (set-face-attribute 'fixed-pitch nil
                        :family "AGVRMP"
                        :height 180)
    (set-face-attribute 'variable-pitch nil
                        :family "AGVRMP"
                        :height 180)
    ;; Japanese
    (set-fontset-font nil
                      'japanese-jisx0213.2004-1
                      (font-spec :family "AGVRMP" :height 180))
    (set-fontset-font nil
                      'japanese-jisx0213-2
                      (font-spec :family "AGVRMP" :height 180))
    (set-fontset-font nil
                      'katakana-jisx0201
                      (font-spec :family "AGVRMP" :height 180))
    ;; Latin with pronounciation annotations
    (set-fontset-font nil
                      '(#x0080 . #x024F)
                      (font-spec :family "AGVRMP" :height 180))
    ;; Math symbols
    (set-fontset-font nil
                      '(#x2200 . #x22FF)
                      (font-spec :family "AGVRMP" :height 180))
    ;; Greek
    (set-fontset-font nil
                      '(#x0370 . #x03FF)
                      (font-spec :family "AGVRMP" :height 180))
    ;; Some Icons
    (set-fontset-font nil
                      '(#xE0A0 . #xEEE0)
                      (font-spec :family "AGVRMP" :height 180))
    ))
(leaf *gui
  :if window-system
  :config
  (set-frame-parameter nil 'alpha 90)
  (setq use-default-font-for-symbols nil)
  (scroll-bar-mode -1)
;;  (my:load-window-config)
  )
  • [ ] FSMRMP のギリシャ文字が全角にならない. 要調整
幅の確認:
Greek, Math, 絵文字は全角, 他は半角で 2:1 になっているかの確認用

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

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

modeline: powerline

(leaf powerline
  :ensure t
  :init
  (defun my:powerline-ddskk ()
    "skkが読み込まれていなくても状態を表示"
    (cond
     ((not (boundp 'skk-modeline-input-mode))
      (setq skk-modeline-input-mode "--[--]:"))
     (t skk-modeline-input-mode)))
  ;;
  (defun my:skk-setup-modeline ()
      "skk-setup-modeline による modeline の更新を無効化"
    (setq skk-indicator-alist (skk-make-indicator-alist))
    (force-mode-line-update t))
  ;;
  (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)))
  :advice (:override skk-setup-modeline my:skk-setup-modeline)
  :custom
  `((powerline-buffer-size-suffix  . nil)
    (powerline-display-hud         . nil)
    (powerline-display-buffer-size . nil)
    (powerline-text-scale-factor   .  1)
    )
  :config
  (defun my:powerline-theme ()
    "Setup the default mode-line."
    (interactive)
    (my:powerline-ddskk)
    (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))
               (lhs (list (powerline-raw (substring skk-modeline-input-mode 2 -1) 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 " ")
                          ))
               (rhs (list (powerline-raw global-mode-string face1 'r)
                          (powerline-vc face1 '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)
  )

theme: modus-theme

最近乗り換えた. じたばたしているのは背景透過と Wanderlust の face の追加, かな? あとはデフォルトの font-lock を(好みに合わせて)入れ替えたり.

(leaf modus-themes
  :ensure t
  :bind ("<f5>" . modus-themes-toggle)
  :init
  (modus-themes-load-themes)
  :config
  (modus-themes-load-vivendi)
  :preface
  (defun my:set-background (&optional frame)
    "Unsets the background color for transparency"
    (or frame
        (setq frame (selected-frame)))
    (if (display-graphic-p frame)
        (progn
          (set-frame-parameter nil 'alpha '(80  50))
          (add-to-list 'default-frame-alist '(alpha 80 50)))
      (set-face-background 'default "unspecified-bg" frame)))
  ;; (my:modus-themes-custom-faces)
  (defun my:modus-themes-custom-faces ()
    (modus-themes-with-colors
      (custom-set-faces
       ;;
       ;; `(modus-themes-hl-line ((,class ,@(modus-themes--hl-line
       ;;                                    bg-hl-line bg-hl-line-intense
       ;;                                    bg-hl-line-intense-accent blue-nuanced-bg
       ;;                                    bg-region blue-intense-bg
       ;;                                    fg-alt cyan-intense)
       ;;                                 :extend t)))
       ;; `(hl-line ((,class :background "#556b2f")))
       ;;
       ;; preprocessor → builtin
       `(font-lock-builtin-face ((,class ,@(modus-themes--syntax-extra
                                              blue-alt-other blue-alt-other-faint
                                              blue-alt blue-alt-faint))))
       ;; preprocessor:: red
       `(font-lock-preprocessor-face ((,class :inherit modus-themes-bold
                                         ,@(modus-themes--syntax-extra
                                            red-alt red-alt-faint
                                            red-intense red-faint))))
       ;; constant → function
       `(font-lock-function-name-face ((,class ,@(modus-themes--syntax-extra
                                             blue-alt-other blue-alt-other-faint
                                             magenta-alt-other magenta-alt-other-faint))))
       ;; constant :: custom
       `(font-lock-constant-face ((,class ,@(modus-themes--syntax-extra
                                             orange-intense orange-intense
                                             orange-intense orange-intense))))
       ;; keyword → string
       `(font-lock-string-face ((,class :inherit modus-themes-bold
                                        ,@(modus-themes--syntax-extra
                                           magenta-alt-other magenta-alt-other-faint
                                           cyan cyan-faint))))
       ;; keyword :: custom
       `(font-lock-keyword-face ((,class :inherit modus-themes-bold
                                         ,@(modus-themes--syntax-extra
                                            red-alt-other red-alt-other-faint
                                            red-alt-other red-alt-other-faint))))
       ;; type → negation-char
       `(font-lock-negation-char-face ((,class :inherit modus-themes-bold
                                      ,@(modus-themes--syntax-foreground
                                         cyan-alt-other cyan-alt-faint))))
       ;; type :: custom
       `(font-lock-type-face ((,class :inherit modus-themes-bold
                                      ,@(modus-themes--syntax-foreground
                                         yellow-intense yellow-faint))))
       `(font-lock-regexp-grouping-backslash ((,class :inherit bold
                                                      ,@(modus-themes--syntax-string
                                                         fg-escape-char-backslash fg-escape-char-backslash
                                                         fg-escape-char-backslash fg-escape-char-backslash
                                                         ))))
       `(font-lock-regexp-grouping-construct ((,class :inherit bold
                                                      ,@(modus-themes--syntax-string
                                                         fg-escape-char-construct fg-escape-char-construct
                                                         fg-escape-char-construct fg-escape-char-construct
                                                         ))))
       ;; カスタマイズ済みなので上書き
       `(org-agenda-date-weekend
         ((,class :inherit 'default :foreground "#ff7f7f" :bold t)))
       `(org-agenda-date
         ((,class :inherit 'default :foreground "#ffffff" :bold t )))
       `(org-agenda-date-today
         ((,class :inherit 'default :foreground "#ffffff" :bold t :underline)))
       ;; 不要
       `(org-block-begin-line ((,class :background nil)))
       `(org-block-end-line ((,class :background nil)))
       ;; shell の lscolors に揃える.
       `(dired-directory ((,class :foreground "#7f7fff" :bold t)))
       ;;
       ;; wanderlust
       `(wl-highlight-folder-closed-face                  ((,class ,@(modus-themes--syntax-string green-alt-other green-alt-other green-alt-other green-alt-other ))))
       `(wl-highlight-folder-few-face                     ((,class ,@(modus-themes--syntax-string red-alt-other red-alt-other red-alt-other red-alt-other ))))
       `(wl-highlight-folder-killed-face                  ((,class ,@(modus-themes--syntax-string fg-alt fg-alt fg-alt fg-alt ))))
       `(wl-highlight-folder-many-face                    ((,class ,@(modus-themes--syntax-string magenta magenta magenta magenta ))))
       `(wl-highlight-folder-opened-face                  ((,class ,@(modus-themes--syntax-string cyan-alt-other cyan-alt-other cyan-alt-other cyan-alt-other ))))
       `(wl-highlight-folder-path-face                    ((,class :underline t :bold t)))
       `(wl-highlight-folder-unknown-face                 ((,class ,@(modus-themes--syntax-string cyan-alt cyan-alt cyan-alt cyan-alt ))))
       `(wl-highlight-folder-unread-face                  ((,class ,@(modus-themes--syntax-string blue-alt blue-alt blue-alt blue-alt ))))
       `(wl-highlight-folder-zero-face                    ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       `(wl-highlight-message-citation-header             ((,class ,@(modus-themes--syntax-string green-active green-active green-active green-active))))
       `(wl-highlight-message-cited-text-1                ((,class ,@(modus-themes--syntax-string green-intense green-intense green-intense green-intense))))
       `(wl-highlight-message-cited-text-2                ((,class ,@(modus-themes--syntax-string yellow-intense yellow-intense yellow-intense yellow-intense))))
       `(wl-highlight-message-cited-text-3                ((,class ,@(modus-themes--syntax-string blue-intense blue-intense blue-intense blue-intense))))
       `(wl-highlight-message-cited-text-4                ((,class ,@(modus-themes--syntax-string cyan-intense cyan-intense cyan-intense cyan-intense))))
       `(wl-highlight-message-cited-text-5                ((,class ,@(modus-themes--syntax-string purple-intense purple-intense purple-intense purple-intense))))
       `(wl-highlight-message-cited-text-6                ((,class ,@(modus-themes--syntax-string red-intense red-intense red-intense red-intense))))
       `(wl-highlight-message-cited-text-7                ((,class ,@(modus-themes--syntax-string green-intense green-intense green-intense green-intense))))
       `(wl-highlight-message-cited-text-8                ((,class ,@(modus-themes--syntax-string yellow-intense yellow-intense yellow-intense yellow-intense))))
       `(wl-highlight-message-cited-text-9                ((,class ,@(modus-themes--syntax-string blue-intense blue-intense blue-intense blue-intense))))
       `(wl-highlight-message-cited-text-10               ((,class ,@(modus-themes--syntax-string cyan-intense cyan-intense cyan-intense cyan-intense))))
       `(wl-highlight-message-header-contents             ((,class ,@(modus-themes--syntax-string purple-intense purple-intense purple-intense purple-intense))))
       `(wl-highlight-message-headers                     ((,class ,@(modus-themes--syntax-string cyan-intense cyan-intense cyan-intense cyan-intense))))
       `(wl-highlight-message-important-header-contents   ((,class ,@(modus-themes--syntax-string magenta-intense magenta-intense magenta-intense magenta-intense))))
       `(wl-highlight-message-important-header-contents2  ((,class ,@(modus-themes--syntax-string magenta-intense magenta-intense magenta-intense magenta-intense))))
       `(wl-highlight-message-signature                   ((,class ,@(modus-themes--syntax-string fg-dim fg-dim fg-dim fg-dim))))
       `(wl-highlight-message-unimportant-header-contents ((,class ,@(modus-themes--syntax-string fg-dim fg-dim fg-dim fg-dim))))
       `(wl-highlight-summary-answered-face               ((,class ,@(modus-themes--syntax-string green-intense green-intense green-intense green-intense))))
       `(wl-highlight-summary-deleted-face                ((,class ,@(modus-themes--syntax-string blue-intense blue-intense blue-intense blue-intense))))
       `(wl-highlight-summary-disposed-face               ((,class ,@(modus-themes--syntax-string fg-dim fg-dim fg-dim fg-dim))))
       `(wl-highlight-summary-flagged-face                ((,class ,@(modus-themes--syntax-string yellow-intense yellow-intense yellow-intense yellow-intense))))
       `(wl-highlight-summary-high-unread-face            ((,class ,@(modus-themes--syntax-string red-intense red-intense red-intense red-intense))))
       `(wl-highlight-summary-low-unread-face             ((,class ,@(modus-themes--syntax-string red-intense red-intense red-intense red-intense))))
       `(wl-highlight-summary-normal-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       `(wl-highlight-summary-refiled-face                ((,class ,@(modus-themes--syntax-string blue-alt blue-alt blue-alt blue-alt))))
       `(wl-highlight-summary-spam-face                   ((,class ,@(modus-themes--syntax-string magenta-intense magenta-intense magenta-intense magenta-intense))))
       `(wl-highlight-summary-thread-top-face             ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       `(wl-highlight-summary-unread-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-header-separator-face               ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-logo-face                           ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-copied-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       `(wl-highlight-summary-displaying-face             ((,class :underline t :bold t)))
       ;; `(wl-highlight-summary-resend-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-prefetch-face               ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-target-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-temp-face                   ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       `(wl-highlight-summary-new-face                    ((,class ,@(modus-themes--syntax-string red-intense red-intense red-intense red-intense))))
       ;; `(wl-highlight-summary-killed-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-low-read-face               ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-thread-indent-face                  ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-forwarded-face              ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-highlight-summary-high-read-face              ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-message-header-narrowing-face                 ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;; `(wl-summary-persistent-mark-face                  ((,class ,@(modus-themes--syntax-string fg-main fg-main fg-main fg-main))))
       ;;
       )))
  :advice
  ((:after
    modus-themes-load-vivendi
    my:set-background)
   (:after
    modus-themes-load-operandi
    (lambda () (set-face-background 'default "#FFFFFF" )))
   )
  :hook
  `((modus-themes-after-load-theme-hook
     . (lambda ()
         (my:modus-themes-custom-faces)
         ))
    (window-setup-hook . my:set-background)
    (tty-setup-hook . my:set-background)
    )
  :custom
  `(
    (modus-themes-inhibit-reload          . t)
    (modus-themes-success-deuteranopia    . nil) ;; default
    (modus-themes-bold-constructs         . nil) ;; default
    (modus-themes-italic-constructs       . nil) ;; default
    (modus-themes-syntax                  . '(alt-syntax green-strings))
    (modus-themes-no-mixed-fonts          . t)
    (modus-themes-links                   . '(neutral-underline bold))
    (modus-themes-prompts                 . '(intense bold))
    (modus-themes-mode-line               . '(borderless accented))
    (modus-themes-tabs-accented           . t)
    (modus-themes-completions             . nil) ;; default
    (modus-themes-mail-citations          . nil) ;; default
    (modus-themes-fringes                 . nil) ;; default
    (modus-themes-lang-checkers           . '(straight-underline intense))
    (modus-themes-hl-line                 . '(accented intense))
    (modus-themes-subtle-line-numbers     . nil) ;; default
    (modus-themes-paren-match             . '(bold intense))
    (modus-themes-region                  . '(accented no-extend))
    (modus-themes-diffs                   . nil) ;; default
    (modus-themes-org-blocks              . nil) ;; default
    (modus-themes-org-agenda              . '((t . nil)))
    (modus-themes-headings                . '((t . rainbow)))
    (modus-themes-scale-headings          . nil)
    (modus-themes-variable-pitch-ui       . nil)
    (modus-themes-variable-pitch-headings . nil)
    ;; (modus-themes-vivendi-color-overrides
    ;;  . '(
    ;;      (bg-hl-line-intense-accent . "#556b2f")
    ;;      ))
    )
  )

Debug&Test

(leaf rg
  :if (executable-find "rg")
  :ensure t
  )
;;(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
  :hook
  (vterm-mode-hook
   . (lambda () (setq-local global-hl-line-mode nil)))
  )
(leaf keg :ensure t)
(leaf keg-mode :ensure t)
(leaf esup
  :ensure t
  :custom
  ((esup-insignificant-time . 0.01)
   (esup-depth              . 0)) ;; 🤔
  )

起動時間の出力

起動時間を計測する 改訂版 - すぎゃーんメモ

(leaf *show-startup-time
  :hook
  (emacs-startup-hook
   . (lambda ()
       (message "init time: %.3f sec"
                (float-time (time-subtract after-init-time before-init-time)))))
  )

最後に

profiler report

これも必要に応じて

;; (profiler-report)
;; (profiler-stop)

provide の設定

(provide 'init)
;; Local Variables:
;; byte-compile-warnings: (not obsolete cl-functions)
;; 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/>.