Emacs の設定


Related Index Emacs

はじめに

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

基本方針は以下の通り:

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

:Eating your own dog food - Wikipedia

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

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

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

設定は 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 -*-

ディレクトリ構成の修正

分割した設定ファイル群やパッケージで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/      ←  (基本的に)参照するだけの資源置き場所
     `-- tmp/        ←  一次ファイルの置き場所

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

(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 "tmp/" user-emacs-directory))
(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))
(dolist (my:d '(my:d:share
                my:d:tmp
                my:d:pkg:elpa
                my:d:pkg:elget))
  (lambda ()
    (unless (file-directory-p my:d)
      (make-directory my:d t))))

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

(defconst my:d:org (concat (getenv "HOME") "/Nextcloud/org/"))

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

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

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

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

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

package.el

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

(require 'package nil 'noerror)
;; elpa/gnutls workaround
(if (string< emacs-version "26.3")
    (setq gnutls-algorithm-priority "NORMAL:-VERS-TLS1.3"))
(setq package-enable-at-startup t
      package-user-dir my:d:pkg:elpa
      package-gnupghome-dir (expand-file-name ".gnupg" (getenv "HOME"))
      package-archives
      '(("gnu"   . "https://elpa.gnu.org/packages/")
        ("melpa" . "https://melpa.org/packages/")
        ;; ("org"   . "https://orgmode.org/elpa/")
        )
      )
(eval-when-compile
  (unless (file-exists-p (locate-user-emacs-file "tmp/bootstrap-stamp"))
    (package-refresh-contents)
    (with-temp-buffer (write-file (locate-user-emacs-file "tmp/bootstrap-stamp")))
    ))
(package-initialize)

leaf.el

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

(unless (package-installed-p 'leaf)
  (package-install 'leaf t))
;; (require 'leaf nil 'noerror)
(leaf leaf-keywords
  :ensure t
  :init
  (leaf blackout :ensure t)
  (leaf el-get
    :ensure t
    :require 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))
    )
  :config
  (leaf-keywords-init)
  )

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

shell(zsh)で設定した PATH などの環境変数をEmacsに引き継ぐために purcell/exec-path-from-shell を使います. 今の所

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

を読み込んでいます(多いな…).

(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-variables           . '("SHELL"
                                                 "DEBFULLNAME"
                                                 "DEBEMAIL"
                                                 "SKKSERVER"
                                                 "TEXMFHOME"
                                                 "http_proxy"
                                                 "GPG_KEY_ID"
                                                 "GPG_AGENT_INFO"
                                                 "PASSWORD_STORE_DIR"
                                                 "PATH")))
  :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")))
    nil)
  )

認証関連: 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))
  :preface
  (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))
  :init
  (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")))
    )
  )

独自関数

細かい独自関数, など.

dpkg-status

もっと良い方法がありそうなモンですが.

(defun my:dpkg-status (package)
  "Return the `PACKAGE' status from dpkg --get-selections."
  (string-match "^ii" (shell-command-to-string (format "dpkg -l %s" package))))

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

zsh prompt風味.

(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)))))
  :config
  (if (not (memq 'my:delete-file-if-no-contents after-save-hook))
      (setq after-save-hook
            (cons 'my:delete-file-if-no-contents after-save-hook)))
  )

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

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

(leaf *keepscratchbuffer
  :preface
  (defun my:make-scratch (&optional arg)
    (interactive)
    (progn
      ;; "*scratch*" を作成して buffer-list に放り込む
      (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 ()
    (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)))))
  )

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

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

言語の設定

最近の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 にある設定は極力こちらに移すようにしている.

  • gc-cons-threshold はとりあえずデフォルトのままに.
    • メモリ喰いな拡張を入れている場合には, 安易に gc-cons-threshold を上げるのは考えものである. 「gc が走る→大きな領域を掃除するのでその間 emacs が止まる」 という事を頻繁に経験することになるだろう.
    • idle-timer で入力が無い時に GC を走らせることにしてみた.
  • 大抵の場合ターミナル内で -nw として起動するし, メニューは触ったことないので使わないので, フレーム, ツールバー等を非表示にする.
  • .elc.el の timestamp を比較し, 新しい方を読み込む (load-prefer-newer は Emacs >= 24.4 から).
  • yes or no を y or n に

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

(leaf cus-start
  :custom
  `(
    ;; (gc-cons-threshold       . ,(* 1024 1024 1024)) ;; 1GiB
    ;; (garbage-collection-messages . t)
    ;; 表示
    (tool-bar-mode          . nil)  ; 基本 "-nw" なので不要
    (scroll-bar-mode        . nil)  ; 基本 "-nw" なので不要
    (menu-bar-mode          . nil)  ; 基本 "-nw" なので不要
    (blink-cursor-mode      . nil)  ; 基本 "-nw" なので不要
    (column-number-mode     . nil)  ; 基本 "-nw" なので不要
    (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         . t)
    (kept-new-versions       . 5)
    (kept-old-versions       . 5)
    (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)
  ;; GC
  ;; Run GC every 60 seconds if emacs is idle.
  (run-with-idle-timer 60.0 t #'garbage-collect)
  )

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
                                free-vars
                                unresolved
                                callargs
                                redefine
                                obsolete
                                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))
  :global-minor-mode
  global-auto-revert-mode
  )

savehist: 変更履歴を保存

(leaf savehist
  :custom
  `((savehist-file   . ,(expand-file-name "history" my:d:tmp)))
  :hook
  ((emacs-startup-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:share))
    ;; 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))
  :config
  (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 (after-init-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)
  :config
  (cond ((executable-find "xdg-open")
         (setq browse-url-browser-function 'browse-url-xdg-open
               shr-external-browser 'browse-url-xdg-open))
        ((eq system-type 'darwin)
         (setq browse-url-browser-function 'browse-url-default-macosx-browser
               shr-external-browser '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
  :require t
  :config
  (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)
  )

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

ファイルを開く際には my:counsel-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"
                                 )))
  )

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

SOMEDAY Windows(WSL2)対応 [/]

;; nothing - -;
  • [ ] 使う気になったら何かしなければならない, 気がしている

カレンダー設定

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

(leaf calendar
  ;; :require t
  :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))
  )
(leaf japanese-holidays
  :ensure t
  :after calendar
  :hook ((calendar-today-visible-hook   . japanese-holiday-mark-weekend)
         (calendar-today-invisible-hook . japanese-holiday-mark-weekend)
         (calendar-today-visible-hook   . calendar-mark-today))
  :custom
  ((japanese-holiday-weekend         . '(0 6))
   (japanese-holiday-weekend-marker  . '(holiday  ;; 日
                                         nil      ;; 月
                                         nil      ;; 火
                                         nil      ;; 水
                                         nil      ;; 木
                                         nil      ;; 金
                                         japanese-holiday-saturday)))
  :config
  ;; これは最後にやらないとだめ? with-eval-after-load 的な?
  (setq calendar-holidays (append japanese-holidays holiday-local-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")))
  :config
  (migemo-init)
  )

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)
  )
  • [ ] 背景色の指定
  • [ ] 幅の強制

SOMEDAY elscreen: バッファー用 GNU screen もどき [0/2]

emacs-jpのfolk版を利用中. modeline の表示そのものは無効化しておく.

(leaf elscreen
  :ensure t
  :custom
  ((elscreen-display-tab             . 8)
   (elscreen-tab-display-control     . nil)
   (elscreen-display-screen-number   . nil)
   (elscreen-prefix-key              . "") ;; なんかカッコ悪いなぁ...
   )
  :config
  (elscreen-start)
  )
;; (leaf elscreen-multi-term
;;   :el-get  wamei/elscreen-multi-term
;;  )
  • [ ] Debian パッケージ版は古い.更新すべき

ついでに elscreen⇔ zsh での連携を設定しておく. 詳細は

などを参照のこと.

(defun return-current-working-directory-to-shell ()
  (interactive)
  (expand-file-name
   (with-current-buffer
       (if (featurep 'elscreen)
           (let* ((frame-confs (elscreen-get-frame-confs (selected-frame)))
                  (num (nth 1 (assoc 'screen-history frame-confs)))
                  (cur-window-conf
                   (assoc 'window-configuration
                          (assoc num (assoc 'screen-property frame-confs))))
                  (marker (nth 2 cur-window-conf)))
             (marker-buffer marker))
         (nth 1
              (assoc 'buffer-list
                     (nth 1 (nth 1 (current-frame-configuration))))))
     default-directory)))
  • [ ] counsel-find-file だと動作しないことがある. 謎

日本語入力: 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
  :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 を扱えれば良いのだけれども. 辞書サーバの指定は以下.

(cond
 ((getenv "SKKSERVER")
  (setq skk-server-host "127.0.0.1"
        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)

インクリメンタルサーチ

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

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

補完: ido

補完は ido が軽いし速いので好み. 見た目がちょっと簡素すぎる気もするので、なんとか弄りたい所ではある。

ido の設定

(leaf ido
  :init
  (leaf ido-completing-read+ :ensure t)
  ;; (leaf ido-describe-bindings :ensure t)
  :bind (("C-x C-f" . ido-find-file))
  :preface
  (defun my:ido-disable-line-trucation ()
    (set (make-local-variable 'truncate-lines) nil))
  ;;
  (defun my:ido-remove-tramp-from-cache nil
    "Remove any TRAMP entries from `ido-dir-file-cache'.
    This stops tramp from trying to connect to remote hosts on emacs startup,
    which can be very annoying."
    (interactive)
    (setq ido-dir-file-cache
          (cl-remove-if
           (lambda (x)
             (string-match "/\\(rsh\\|ssh\\|telnet\\|su\\|sudo\\|sshx\\|krlogin\\|ksu\\|rcp\\|scp\\|rsync\\|scpx\\|fcp\\|nc\\|ftp\\|smb\\|adb\\):" (car x)))
           ido-dir-file-cache)))
  ;;
  (defun my:ido-kill-emacs-hook ()
    (my:ido-remove-tramp-from-cache)
    (ido-save-history))
  :advice (:override ido-kill-emacs-hook
                     my:ido-kill-emacs-hook)
  :hook ((ido-setup-hook
          . (lambda ()
              (define-key ido-completion-map (kbd "C-h") 'ido-delete-backward-updir)
              (define-key ido-completion-map (kbd "C-l") 'ido-delete-backward-updir)))
         (ido-minibuffer-setup-hook . my:ido-disable-line-trucation))
  :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"
             )
           do (add-to-list 'completion-ignored-extensions ext))
  :custom
  `(;; prefix match 入力を先頭一致可能
    (ido-enable-prefix             . nil)
    ;; TAB で名前の一致までその後 RET で実行
    (ido-confirm-unique-completion . t)
    ;; . を prefix として扱う
    (ido-enable-dot-prefix         . t)
    ;; flx matching を試す際には prefix を無効化すること
    (ido-enable-flex-matching      . t)
    ;;
    (ido-default-file-method       . 'selected-window)
    (ido-default-buffer-method     . 'selected-window)
    ;; avoid [Too Big]
    (ido-max-directory-size        . 1000000)
    ;; Tramp では無効化
    (ido-enable-tramp-completion   . nil)
    ;; ido-vertical も調べないとアカン様な.
    (ido-use-faces                 . t)
    ;; 拡張子には match しない
    (ido-ignore-extensions         . t)
    ;; 補完できない場合の挙動 ← "何もしない" は可能かな? nil?
    (ido-cannot-complete-command   . 'ido-next-match)
    ;; 補完候補が無い場合に search を始める長さ ⇒ -1 で停止
    (ido-auto-merge-work-directories-length . -1)
    ;; 補完候補が無い場合に search を始める時間
    ;; run-with-timer に与える値なので, とりあえず長め(30分)にしておく
    (ido-auto-merge-delay-time     . 1800)
    ;; 補完結果の保存
    (ido-save-directory-list-file
     . ,(expand-file-name "ido.last" my:d:tmp)))
  :config
  (ido-mode t)
  ;; (ido-everywhere t)   ; <- 過去の選択を覚えている奴と相性悪い.
  )

flx-ido: flex match の強化

曖昧マッチが非常に直感的になった.地味に便利で手放せない.

(leaf flx-ido
  :ensure t
  :custom
  `((flx-ido-use-faces . t)
    (flx-ido-threshold . 10000))
  :config
  (flx-ido-mode 1)
  )

ido-flx-with-migemo: flx + migemo

これは便利だ

(leaf ido-flex-with-migemo
  :if (executable-find "cmigemo")
  :ensure t
  :after (flx migemo)
  :config
  (ido-flex-with-migemo-mode)
  )

SOMEDAY ido-vertical-mode: 候補を縦に並べて表示 [/]

(leaf ido-vertical-mode
  :ensure t
  :after all-the-icons-in-terminal
  :preface
  (defface my:ido-indicator
    '((((class color) (background light)) :foreground "#FFBF7F")
      (((class color) (background dark)) :foreground "#FFBF7F"))
    "Face used by Ido highlighting the arrow.")
  :custom
  `(;; 補完候補数の表示
    (ido-vertical-show-count  . t)
    ;; キーバインド
    (ido-vertical-define-keys . 'C-n-C-p-up-down-left-right)
    ;; minibuffer の高さ制限
    (ido-max-window-height    . 0.3)
    ;; 候補選択表示のカスタマイズ
    (ido-vertical-indicator
     . ,(format "%s"
                (all-the-icons-faicon "hand-o-right"
                                      :face 'my:ido-indicator)))
    )
  :config
  (ido-vertical-mode 1)
  )
  • [ ] Debian パッケージは古い.更新すること

amx

M-x を ido で. 以前は smex を利用していたが, amx に乗り換えた.

(leaf amx
  :bind (("M-x" . amx))
  :ensure t
  :custom
  `(;; backend 'ivy or 'ido or 'auto
    (amx-backend . 'ido)
    ;; prompt
    (amx-prompt-string . "amx: ")
    ;; keybind
    (amx-show-key-bindings . nil)
    ;; 履歴
    (amx-save-file
     . ,(expand-file-name "amx-items" my:d:tmp))
    (amx-history-length . 128))
  )

ido-recentf: recentf を ido で

"recentf-list" の結果を整形して, minibuffer に納まる様に縮小したり

(leaf *ido-recentf-open
  :preface
  (defun ido-recentf-open ()
    "Use `ido-completing-read' to \\[find-file] a recent file"
    (interactive)
    (let ((files (mapcar (lambda (f)
                           (cons (my:shorten-file-path f 70) f))
                         recentf-list)))
      (let ((selected (ido-completing-read "Files: " (mapcar #'car files))))
        (find-file (assoc-default selected files)))))
  :bind ("C-x C-r" . ido-recentf-open)
  )

ido-ghq

ghq list を ido で選んで dired で開く

(leaf ido-ghq
  :if (executable-find "ghq")
  :el-get uwabami/ido-ghq
  :bind (("C-x f" . ido-ghq-open))
  :config
  (setq ido-ghq-short-list t)
  )

校正, 辞書等

SOMEDAY redpen-paragraph: redpen による文章校正 [/]

(leaf redpen-paragraph
  :disabled t
  :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"
                                )))
                        ))
  :config
  ;; default
  (setq 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 (my:dpkg-status "lookup-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 "研究社 英和活用大辞典 ")))
  )

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 (and (or (my:dpkg-status "wl")
               (my:dpkg-status "wl-beta"))
           (my:dpkg-status "rail"))
  :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))
  :advice (:override wl-mode-line-buffer-identification
                     my:wl-mode-line-buffer-identification)
  :init
  (define-mail-user-agent
    'wl-user-agent
    'wl-user-agent-compose
    'wl-draft-send
    'wl-draft-kill
    'mail-send-hook)
  (setq elmo-msgdb-directory "~/.cache/wanderlust"
        elmo-maildir-folder-path "~/.cache/wanderlust"
        elmo-cache-directory "~/.cache/wanderlust"
        wl-score-files-directory "~/.cache/wanderlust"
        wl-init-file (concat user-emacs-directory "init-wl")
        mail-user-agent 'wl-user-agent
        read-mail-command 'wl)
  (unless (file-directory-p elmo-msgdb-directory)
    (make-directory elmo-msgdb-directory))
  (unless (file-directory-p (concat elmo-msgdb-directory "/local"))
    (make-directory (concat elmo-msgdb-directory "/local")))
  (unless (file-directory-p (concat elmo-msgdb-directory "/local/Trash"))
    (make-directory (concat elmo-msgdb-directory "/local/Trash")))
  :config
  )

割と /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)
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 に丸投げ.

;; (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
      mime-play-delete-file-immediately 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)))))
クォートされた文字列もデコードする
(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))
;; (mime-set-field-decoder
;;  'From nil 'eword-decode-and-unfold-unstructured-field-body)
;; (mime-set-field-decoder
;;  'To nil 'eword-decode-and-unfold-unstructured-field-body)
(with-eval-after-load 'eword-decode
  (mime-set-field-decoder
   'From 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
        (elmo-replace-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-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-from-header-contents
                '((t (:foreground "#FFFF4C" :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)
メール本文の文字コード

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

(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 'turn-on-orgtbl)
c-sig

署名の選択に c-sig を使用している. 設定は以下の通り. Mew 風に C-c <tab> で signature を挿入するようにしている

(leaf c-sig
  :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 2000000000)
(setq elmo-multi-number 2000000000)

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

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

(leaf org
  :blackout `((org-mode . ,(all-the-icons-icon-for-mode 'org-mode)))
  :init
  ;; (leaf org-plus-contrib :ensure t)
  :bind (("C-x n s" . org-narrow-to-subtree)
         ("C-x n w" . widen))
  :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                  . my:org-timestamp-hook)
   (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)" "|" "DONE(d)" "CANCEL(c)" "SOMEDAY(s)")
                                    (type "ARTICLE(a)")
                                    (type "MEMO(m)")))
    ;; 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)))
  ;; (with-eval-after-load "org"
  ;;   (setq org-file-apps '((auto-mode . emacs)
  ;;                         ("\\.mm\\'" . default)
  ;;                         ("\\.x?html?\\'" . default)
  ;;                         ("\\.pdf\\'" . "xdg-open %s")))
  ;;   )
  )

Org-Tempo: テンプレート補完/展開

(leaf org-tempo)

Org-Id

(leaf org-id
  :init
  (leaf org-macs :require t)
  :custom
  `((org-id-locations-file . ,(expand-file-name "org-id-locations" my:d:share))
    ;; (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))))))
  ;;
  (add-hook 'before-save-hook '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))
  )

Agenda: スケジュール, TODO 表示

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

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

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

(leaf org-agenda
  :bind (("C-c a" . org-agenda))
  :init
  (require 'japanese-holidays)
  ;;
  (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)))))
  ;; font-lock の適用
  (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 "MHC:" 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)))
      (goto-char (point-min))
      (while (re-search-forward "祝日:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face org-agenda-date-weekend)))
      (goto-char (point-min))
      (while (re-search-forward "休日:" nil t)
        (add-text-properties (match-beginning 0) (point-at-eol)
                             '(face org-agenda-date-weekend)))))
  :hook
  ;; 色付け
  ((org-agenda-finalize-hook . my:org-agenda-finalize-font-lock))
  :custom
  (;; day or week
   (org-agenda-span              . 'day)
   ;; YY/MM/DD (曜)
   (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)
   ;; GTD 用の設定
   (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")
          ))
        )))
  :config
  ;; 使用するファイル
  (dolist (file
           '("Archive.org"
             "Holidays.org"
             "Memo.org"
             "Schedule.org"
             "MHC.org"
             "GFD.org"
             "Univ.org"
             "DebianJP.org"
             "twitter.org"
             "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"))
  (if (file-directory-p
       (expand-file-name "Public/" (getenv "HOME")))
      (dolist (file
               (directory-files-recursively
                (expand-file-name "Public/" (getenv "HOME")) "org$"))
        (add-to-list 'org-agenda-files file)))
  (if (file-directory-p
       (expand-file-name "journal/" my:d:org))
      (dolist (file
               (directory-files-recursively
                (expand-file-name "journal/" my:d:org) "org$"))
        (add-to-list 'org-agenda-files file)))
  )

Org-journal: 日記

機能が豊富なのだが, イマイチ使いこなせていない.

(leaf org-journal
  :if (and (file-directory-p my:d:org)
           (>= emacs-major-version 25))
  :bind (("C-x M"    . my:org-journal-new-entry)
         (:org-journal-mode-map
          ("C-c C-c" . my:org-journal-entry-save-and-exit)))
  :ensure t
  :init
  (defun my:org-journal-new-entry (prefix &optional time)
    "Add new journal entry on new elscreen"
    (interactive "P")
    (elscreen-create)
    (org-journal-new-entry nil time))
  ;;
  (defun my:org-journal-entry-save-and-exit ()
    "Save journal entry and close elscreen buffer"
    (interactive)
    (save-buffer)
    (elscreen-kill))
  :hook
  ((org-journal-mode-hook
    . (lambda()
        (setq-local truncate-lines t))))
  :custom
  `((org-journal-file-type                 . 'yearly)
    (org-journal-dir                       . ,(expand-file-name "journal" my:d:org))
    (org-journal-file-format               . "%Y.org")
    (org-journal-cache-file
     . ,(expand-file-name "org-journal.cache" my:d:tmp))
    (org-journal-date-format               . "%x (%a)")
    (org-journal-time-format               . "<%Y-%m-%d %R> ")
    (org-journal-time-prefix               . "** MEMO ")
    (org-journal-enable-agenda-integration . t)
    (org-journal-find-file                 . 'find-file)
    (org-journal-carryover-delete-empty-journal . 'ask)
    (org-journal-start-on-weekday          . 0) ;; sunday
    )
  :config
  (eval-and-compile 'browse-url)
  (with-eval-after-load 'org-journal
    (global-set-key (kbd "C-c C-j") 'browse-url-at-point))
  )

Org-capture: メモ取り

キーバインドは以前 changelog memo をやっていた時の癖で C-x m をメモにしている.

(leaf org-capture
  :if (file-directory-p my:d:org)
  :init
;;  (leaf org-wl
;;    :if (or (my:dpkg-status "wl")
;;            (my:dpkg-status "wl-beta"))
;;    :el-get uwabami/org-wl
;;    )
  :bind (("C-x m" . org-capture))
  :pl-setq
  (my:org:calendar1 my:org:calendar2)   ;; 名前がイケてないっ!
  :config
  (defun my:org-journal-add-date-entry-capture ()
    (org-journal-new-entry t)
    (goto-char (point-max))
    )
  (setq org-default-notes-file (expand-file-name "Memo.org" my:d:org))
  (setq org-capture-templates
        `(
          ("t" "TODO" plain
           (function my:org-journal-add-date-entry-capture)
           "** TODO %(format-time-string org-journal-time-format)%^{title} %^g\n  %?\n  %a"
           :prepend nil
           :unnarrowed nil
           :kill-buffer t
           )
          ("m" "メモを追加" plain
           (function my:org-journal-add-date-entry-capture)
           "** MEMO %(format-time-string org-journal-time-format) %?"
           :prepend nil
           :unnarrowed nil
           :kill-buffer t
           )
          ("a" "少し長めの記事を追加する" plain
           (function my:org-journal-add-date-entry-capture)
           "** ARTICLE %(format-time-string org-journal-time-format) %? "
           :prepend nil
           :unnarrowed nil
           :kill-buffer t
           )
          ("s" "個人予定表スケジュールを追加" plain
           (file ,(expand-file-name "Schedule.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar1)\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           :unnarrowed nil
           :kill-buffer t
           )
          ("u" "仕事予定表スケジュールを追加" plain
           (file ,(expand-file-name "Univ.org" my:d:org))
           "* %^{prompt}\n  :PROPERTIES:\n  :calendar-id: %(format \"%s\" my:org:calendar2)\n  :END:\n  :org-gcal:\n%?\n%i\n  :END:"
           :prepend nil
           :unnarrowed nil
           :kill-buffer t
           )
          )
        )
  )

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

request token 等の置き場所の変更 実際の情報等は password-store を使って設定しておく. ついでに agenda 表示の際の色付けを設定.

(leaf org-gcal
  :if (and my:d:password-store
           (file-directory-p my:d:org))
  :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 request
    :ensure t
    :preface
    (setq request-storage-directory (expand-file-name "request" my:d:tmp))
    (unless (file-directory-p request-storage-directory)
      (make-directory request-storage-directory))
    :config
    (setq request-storage-directory (expand-file-name "request" my:d:tmp))
    )
  (leaf persist
    :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    .  30)  ;; 過去 30 日
    (org-gcal-up-days      . 180)  ;; 未来 180 日
    (org-gcal-auto-archive . nil)
    (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))
  :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
    :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とTrelloの連携: org-trello

ToDo管理に Trello を使い始めたので, 設定

(leaf org-trello
  :if (and my:d:password-store
           (file-directory-p my:d:org))
  :ensure t
  :custom
  `((org-trello-files . '(,(expand-file-name "Trello.org" my:d:org))))
  )

Org-Wiki

(leaf org-wiki
  :if (file-directory-p "~/Public/cc-env")
  :el-get (org-wiki
           :type github
           :pkgname "uwabami/org-wiki")
  :custom
  `((org-wiki-location    . "~/Public/cc-env")
   (org-wiki-publish-root . "{{site.url}}/cc-env/")
   (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))
  )

HTML

(leaf ox-html
  :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\\)\\'")))
   )
)

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")
      )
    :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:platex")
    (org-latex-pdf-process   . '("latexmk -pvc %f"))
    (org-latex-classes
     . '(("my:platex"
          "\\documentclass[a4j]{jsarticle}
            [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

Web サイトは Jekyll で作成しています. 以前は org-file を直接 jekyll で処理していましたが, 最近は org を Markdown に export して, それを処理する様にしています.

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

(leaf ox-jekyll
  :if (file-directory-p "~/Public/cc-env")
  :el-get (ox-jekyll
           :type github
           :pkgname "uwabami/ox-jekyll")
  :after (ox-html org-wiki)
  :config
  (defun my:org-wiki-jekyll-finalized1 (contents backend info)
    "Replace some URL"
    (when (org-export-derived-backend-p backend 'jekyll)
      (s-replace
       (format "<a href=\"file://%sREADME.html"
               (expand-file-name user-emacs-directory))
       "<a href=\"{{baseurl}}/cc-env/Emacs.html"
       contents)))
  (add-to-list 'org-export-filter-final-output-functions
               'my:org-wiki-jekyll-finalized1)
  (defun my:org-wiki-jekyll-finalized2 (contents backend info)
    "Replace some URL"
    (when (org-export-derived-backend-p backend 'jekyll)
      (s-replace
       (format "<a href=\"file://%s"
               (expand-file-name "Public/" (getenv "HOME")))
       "<a href=\"{{site.url}}/"
       contents)))
  (add-to-list 'org-export-filter-final-output-functions
               'my:org-wiki-jekyll-finalized2)
  (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)))
  (add-to-list 'org-export-filter-final-output-functions
               'my:org-wiki-jekyll-finalized3)
  (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)))
  (add-to-list 'org-export-filter-final-output-functions
               'my:org-wiki-jekyll-finalized4)
  )

Org-Publish

(leaf org-publish
  :after (ox-jekyll org-wiki)
  :custom
  `((org-publish-timestamp-directory
    . ,(expand-file-name "org-timestamps.el" my:d:tmp)))
  :config
  (defun org-wiki-publish ()
    (interactive)
    (org-publish (org-wiki-make-org-publish-plist
                  'org-jekyll-publish-to-html)
                 t))
  )

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

VCS

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

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

(leaf *git
  :if (>= emacs-major-version 25)
  :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 'ivy
        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+
  :if (>= emacs-major-version 25)
  :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
  :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       . 'ivy)
            (dumb-jump-force-searcher . 'rg))
  )
(leaf smart-jump
  :ensure t
  :config
  (smart-jump-setup-default-registers)
  )

SOMEDAY 言語毎の補完: company-mode

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

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

(leaf company
  :ensure t
  :disabled t
  :blackout t
  :custom
  ((company-idle-delay            . 0)
   (company-minimum-prefix-length . 4)
   (company-selection-wrap-around . t)
   ;; disable some noisy default backends
   (company-backends . '(company-files company-capf
                                       (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)))
   )
  )

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
  :if (<= 26.1 (string-to-number emacs-version))
  :ensure t
  )
  • [ ] 設定調整が必要

LSP: lsp-mode

(leaf lsp-mode
  :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を使う

と言った所. Latexmkの設定にはauctex-latexmkを利用する.

(leaf auctex
  :if (and (executable-find "uplatex")
           (executable-find "latexmk"))
  :init
  (leaf auctex-latexmk
    :el-get tom-tan/auctex-latexmk
    :hook (LaTeX-mode-hook . auctex-latexmk-setup)
    :config
    (setq auctex-latexmk-inherit-TeX-PDF-mode nil
          TeX-command-default "LaTeXMk"
          japanese-LaTeX-command-default "LaTeX"
          )
    )
  ;; :ensure t
  ;; auctex hook
  ;; :config
  )

~/.latexmkrc は以下の通り

#!/usr/bin/env perl
$kanji = defined $ENV{"LATEXENC"} ? "-kanji=$ENV{\"LATEXENC\"}" : "-kanji=utf8" ;
$latex  = "platex -interaction=nonstopmode -src-specials -shell-escape --synctex=1 $kanji" ;
$latex_silent = "platex -interaction=batchmode -src-specials -shell-escape --synctex=1 $kanji" ;
$pdflatex = "ptex2pdf -e -l -ot '-shell-escape -synctex=1 $kanji'";
$bibtex = "pbibtex $kanji";
# disable makeindex/mendex
$makeindex = "touch -m %D";
# if ( $ENV{"LATEXENC"} == "jis" ){
#     $makeindex = "mendex -J %O -c -r -o %D %S ";
# } elsif ( $ENV{"LATEXENC"} == "euc" ){
#     $makeindex = "mendex -E %O -c -r -o %D %S ";
# } elsif ( $ENV{"LATEXENC"} == "sjis" ){
#     $makeindex = "mendex -S %O -c -r -o %D %S";
# } else {
#     $makeindex = "mendex -U %O -c -r -o %D %S";
# }
$dvipdf = "dvipdfmx %O -o %D %S";
$dvips = 'dvips %O -z -f %S | convbkmk -u > %D';
$ps2pdf = 'ps2pdfwr %O %S %D';
$pdf_mode = 3;
$pdf_previewer = 'xdg-open';
# $pdf_previewer = 'fwdevince %S %D';
$pdf_update_method = 1;
$cleanup_mode = 2;
$clean_ext = "snm nav vrb synctex.gz spl bbl blg run.xml pgf-plot.table pgf-plot.gnuplot"

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               . 2)
     (f90-if-indent               . 2)
     (f90-type-indent             . 2)
     (f90-program-indent          . 2)
     (f90-continuation-indent     . 2)
     (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)
         )
  :config
  (setq 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))
  )

その他のモード設定

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

(leaf *misc-mode
  :init
  (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 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 xterm-color [0/1]

色を端末と揃える。

(leaf xterm-color
  :disabled t
  :ensure t
  :config
  (setq xterm-color-names
        ["#000000"    ; black
         "#FF4C4C"    ; red
         "#4Cff4C"    ; green
         "#FFFF4C"    ; yellow
         "#4C4CFF"    ; blue
         "#FF4CFF"    ; magenta
         "#4CFFFF"    ; cyan
         "#AAAAAA"]   ; white
        xterm-color-names-bright
        ["#020202"    ; black
         "#FF7F7F"    ; red
         "#7FFF7F"    ; green
         "#FFFF7F"    ; yellow
         "#7F7FFF"    ; blue
         "#FF7FFF"    ; magenta
         "#7FFFFF"    ; cyan
         "#FFFFFF"]   ; white
        xterm-color-use-bold-for-bright nil
        )
  )
  • [ ] ちゃんと効いてない, ような?

SOMEDAY フォント [0/1]

(defun my:load-window-config ()
  "load window-system specific settings"
  (interactive)
  (when window-system
    (progn
      (set-frame-parameter nil 'alpha 95)
      (set-face-attribute 'default nil
                          :family "FSMRMP"
                          :height 195)
      (set-face-attribute 'fixed-pitch nil
                          :family "FSMRMP"
                          :height 195)
      (set-face-attribute 'variable-pitch nil
                          :family "FSMRMP"
                          :height 195)
      ;; Math symbols
      (set-fontset-font nil
                        '(#x2200 . #x22FF)
                        (font-spec :family "FSMRMP" :height 195))
      ;; Greek
      (set-fontset-font nil
                        '(#x0370 . #x03FF)
                        (font-spec :family "FSMRMP" :height 195))
      ;; Some Icons
      (set-fontset-font nil
                        '(#xE0A0 . #xEEE0)
                        (font-spec :family "FSMRMP" :height 195))
      (setq use-default-font-for-symbols t)
      (set-frame-parameter nil 'alpha 95)
      (toggle-frame-maximized)
      )))
(when (window-system)
  (my:load-window-config))
;;
(defun my:load-side-window-config ()
  "load window-system specific settings"
  (interactive)
  (when window-system
    (progn
      (set-frame-parameter nil 'alpha 95)
      (set-face-attribute 'default nil
                          :family "FSMRMP"
                          :height 150)
      (set-face-attribute 'fixed-pitch nil
                          :family "FSMRMP"
                          :height 150)
      (set-face-attribute 'variable-pitch nil
                          :family "FSMRMP"
                          :height 150)
      ;; Math symbols
      (set-fontset-font nil
                        '(#x2200 . #x22FF)
                        (font-spec :family "FSMRMP" :height 150))
      ;; Greek
      (set-fontset-font nil
                        '(#x0370 . #x03FF)
                        (font-spec :family "FSMRMP" :height 150))
      ;; Some Icons
      (set-fontset-font nil
                        '(#xE0A0 . #xEEE0)
                        (font-spec :family "FSMRMP" :height 150))
      (setq use-default-font-for-symbols t)
      (set-frame-parameter nil 'alpha 95)
      (toggle-frame-maximized)
      )))
  • [ ] FSMRMP のギリシャ文字が全角にならない. 要調整
幅の確認:
Greek, Math, 絵文字は全角, 他は半角で 2:1 になっているかの確認用

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

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

Major, Minor モードの表示のカスタマイズ: blackout

基本個々のモードの設定でカスタマイズしているけれども.

(leaf *modeline-string
  ;; ここでやるより, modeline の設定時に一括で変換した方が良い気もしてきた.
  :blackout ((lisp-interaction-mode
              (all-the-icons-icon-for-mode 'lisp-interaction-mode))
             (emacs-lisp-mode
              (all-the-icons-icon-for-mode 'emacs-lisp-mode))
             (text-mode
              (all-the-icons-icon-for-mode 'text-mode))
             )
  )

theme: doom-theme

これまで弄っていた font-lock を doom-theme をベースに移植中: uwabami/emacs-doom-darkpastel

;; (setq frame-background-mode (frame-parameter nil 'background-mode))
(leaf *doom-themes
  :if (>= emacs-major-version 25)
  :init
  (leaf doom-themes :ensure t)
  (leaf emacs-doom-darkpastel
    :el-get (emacs-doom-darkpastel
             :type github
             :pkgname "uwabami/emacs-doom-darkpastel"
             :prepare (add-to-list 'custom-theme-load-path default-directory)
             )
    )
  :custom
  (doom-darkpastel-set-background . nil)
  :config
  (load-theme 'doom-darkpastel t)
  (eaw-and-emoji-fullwidth)
  )

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))
               (face1 (if active 'mode-line-highlight 'powerline-inactive2))
               ;; (face1 (if active 'powerline-active1 'powerline-inactive2))
               ;; (face2 (if active 'powerline-active2 'powerline-inactive2))
               ;; (face2 (if active 'mode-line-highlight '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-raw (all-the-icons-icon-for-mode 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 face1 (powerline-width rhs))
                  (powerline-render rhs))))))
    )
  (my:powerline-theme)
  ;; (powerline-revert)
  )

起動時間の出力

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

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

Debug

(leaf esup
  :ensure t
  :custom
  ((esup-insignificant-time . 0.01)
   (esup-depth              . 0)) ;; 🤔
  )
(leaf srcery-emacs
  :disabled t
  :el-get (srcery-emacs
           :type github
           :pkgname "uwabami/srcery-emacs"
           :prepare (add-to-list 'custom-theme-load-path default-directory)
           )
  :custom ((srcery-transparent-background . t)
           (srcery-org-height . nil)
           (srcery-bright-black . "#4f4f4f"))
  :config
  (load-theme 'srcery t)
  )
(leaf org-bullets
  :disabled t
  :ensure t
  :custom
   (org-bullets-bullet-list . '(" " "◎ " "□" "・"))
  :hook
  (org-mode-hook
   . (lambda () (org-bullets-mode 1)))
  )

最後に

provide の設定

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