はじめに
本記事は,アドベントカレンダー20日目の記事です.昨日は @hotokuさんの「zetasql-formatter.elを作りました)」でした.
今回の記事では,5日目の記事に触発されて,私も2021年に手を加えた自分の設定について振り返ってみようと思います.なお,例のごとく私の設定は,個人サイトや,GitHubで,自由に閲覧できますので,よろしければご参照ください.

新規導入パッケージ
2021年もいくつのパッケージを導入しましたが,インパクトが大きいのはシンタックスハイライトの仕組みを更新する tree-sitter ですね.今のところ javascript の表示にしか適用していませんが,慣れてきたら他の言語にも順次適用していこうと思っています.
tree-sitter を導入
シンタックスハイライトの仕組みに tree-sitter を使い,Emacs で使えるようにしています.macOS/Windows のどちらでもちゃんと動作しており,オススメですね.
(let* ((elp (expand-file-name
       (concat "~/.emacs.d/" (format "%s" emacs-version) "/el-get/")))
 (ets (concat elp "emacs-tree-sitter/"))
 (tsl (concat elp "tree-sitter-langs/")))
  ;; (add-to-list 'load-path (concat ets "langs"))
  (add-to-list 'load-path (concat ets "core"))
  (add-to-list 'load-path (concat ets "lisp"))
  (add-to-list 'load-path tsl))
(defun my-enable-tree-sitter ()
  (unless (featurep 'tree-sitter)
    (require 'tree-sitter)
    (require 'tree-sitter-hl)
    (require 'tree-sitter-debug)
    (require 'tree-sitter-query)
    (require 'tree-sitter-langs))
  (tree-sitter-hl-mode))
(dolist (hook '(js-mode-hook))
  (add-hook hook #'my-enable-tree-sitter))
grugru.el を導入
入力する候補が決まっているキーワード群について,それらをサイクルさせるようにするパッケージです.例えば, nil, t を高速に切り替えられるようになります.
(global-set-key (kbd "C-9") #'grugru)
(with-eval-after-load "grugru-default"
  (custom-set-faces
   '(grugru-edit-completing-function #'ivy-completing-read)
   '(grugru-highlight-face ((t (:bold t :underline "#FF3333"))))
   '(grugru-highlight-idle-delay 1))
  (add-hook 'grugru-before-hook #'my-unlock-view-mode)
  (add-hook 'grugru-after-hook #'save-buffer)
  (add-hook 'ah-after-move-cursor-hook #'grugru--highlight-remove)
  (grugru-define-on-major-mode 'org-mode 'word '("TODO" "DONE"))
  (grugru-default-setup)
  (grugru-find-function-integration-mode 1)
  (grugru-highlight-mode 1))
mlscroll.el を導入
バッファに表示されているエリアが,ファイルの中のどの辺りなのかをバー状で,ミニバッファに表示してくれます.アクセサリ感覚で導入しています.うるさくないし,実用的にも悪くないと思います.マウスカーソルでバーをつまんで左右に移動させれば,バッファ内の表示位置をずらすこともできます.

(when (require 'mlscroll nil t)
  (custom-set-variables
   '(mlscroll-in-color "#FFA07A") ;; light coral
   '(mlscroll-out-color "#FFFFE0")
   '(mlscroll-width-chars 12))
  (mlscroll-mode 1)
  ;; フレームサイズが変わるとき用の微調整
  (with-eval-after-load "moom"
    (defun my-reload-mlscroll ()
      (mlscroll-mode -1)
      (setq mlscroll-border (ceiling (/ moom-font--size 4.0)))
      (mlscroll-mode 1))
    (add-hook 'moom-font-after-resize-hook #'my-reload-mlscroll)))
command-log-mode.el を導入
入力したコマンドの履歴をバッファに表示してくれます.Emacsのバッファを表示しながらお話する時に便利です.
プレゼンテーションという点で,さらに,キーキャストを実現する keypression.el と, Org Mode のプレゼンツールである org-tree-slide.el と組み合わせることで,視覚的なサポートが完成します.
command-log-mode.el の出力は,フレームの横幅を拡張してできる空間に表示するようにしています.フレームサイズの制御は moom.el で簡単にできます.
(with-eval-after-load "command-log-mode"
  (require 'keypression)
  (require 'moom)
  ;; (setq command-log-mode-window-font-size 0)
  (setq command-log-mode-window-size 60)
  (defun my-command-log-mode-activate ()
    (interactive)
    (keypression-mode 1)
    (unless command-log-mode
      (global-command-log-mode 1))
    (when (require 'moom nil t)
      (moom-delete-windows)
      (moom-change-frame-width 140)
      (moom--stay-in-region)
      (clm/open-command-log-buffer)))
  (defun my-command-log-mode-deactivate ()
    (interactive)
    (keypression-mode -1)
    (when command-log-mode
      (global-command-log-mode -1))
    (when (require 'moom nil t)
      (moom-delete-windows))))
ivy-dired-history を導入
dired でファイルコピーする際,コピー先を ivy のインターフェイスで選択できるようになります.頻繁に使う移動先をリストから選ぶことができます.
(when (require 'ivy-dired-history nil t)
  ;; ivy-dired-history-variable は,session.el で明示的に管理.
  ;; savehist-mode を使用してもよい
  ;; check session-globals-include
  (define-key dired-mode-map "," 'dired))
(with-eval-after-load "session"
  (add-to-list 'session-globals-include 'ivy-dired-history-variable))
(setq session-initialize '(de-saveplace session keys menus places)
      session-globals-include '((kill-ring 100)
                                (session-file-alist 100 t)
                                (file-name-history 200)
                                ivy-dired-history-variable
                                search-ring
                                regexp-search-ring))
elfeed.el を導入
いわゆるRSSリーダを Emacs で使えるようにしました.
(with-eval-after-load "elfeed"
  (when (require 'elfeed-org nil t)
    (elfeed-org)
    (setq rmh-elfeed-org-files (list "~/Dropbox/org/elfeed.org")))
  ;; これで elfeed-feeds が更新される
  ;; その後,M-x elfeed, M-x elfeed-update する
  (when (require 'elfeed-web nil t)
    (setq elfeed-web-data-root (concat my-elget-package-dir "/web"))))
circe.el を導入
いわゆるIRCを Emacs で使えるようにしました.
(with-eval-after-load "cire"
  (setq circe-network-options
        '(("Freenode" :tls t :nick "xxxxxx" :channels ("#emacsconf")))))
OrgMode関連
Org Mode関連の設定は Emacs を使う限り永遠に増えていきそうな気がします.
バッファに表示されるタイトルをちょっと強調
(with-eval-after-load "org"
  ;; タイトルを少し強調
  (custom-set-faces
   '(org-document-title ((t (:foreground "RoyalBlue1" :bold t :height 1.2))))
   '(org-document-info ((t (:foreground "DodgerBlue1" :height 1.0))))))
各ツリーの生成時に自動で id が振られるようにする
プロパティの中に常にユニークなIDが格納されるようにします.すると,後からリンク機能で当該ツリーをたどるときに迷子になりません.
;; org-store-link で heading に自動的に挿入される id を使う
(setq org-id-link-to-org-use-id t)
ブリッツにアルファベットを使えるようにする
アルファベットは26文字なので,それ以上の項目を有するリストの場合は,数値に変更されます.
(setq org-list-allow-alphabetical t)
パスワードをミニバッファで入力する
パスワード入力時に別途ダイアログが表示されるのが気に食わないので,ミニバッファで入力できるように指定しました.
(with-eval-after-load "org-crypt"
  (require 'epa)
  (when (version< "27.0" emacs-version)
    ;; ミニバッファでパスワードを入力する
    (setq epg-pinentry-mode 'loopback)))
revert されるバッファがorgバッファのときに,自動的にドロワをたたむ
普段から (global-auto-revert-mode 1) で編集中のファイルが外部から更新されたときには,自動的にバッファを最新の状態に更新しているのですが,orgバッファが更新されるとドロワが展開されてしまい,ちょっと鬱陶しいです.なので,自動的にたたむようにしました.
(global-auto-revert-mode 1)
;; カーソルが (point-max) に移動してしまう場合は非推奨
(with-eval-after-load "org"
  (defun my-org-hide-drawers-all ()
    (when (eq major-mode 'org-mode)
      (org-cycle-hide-drawers 'all)))
  (add-hook 'after-revert-hook 'my-org-hide-drawers-all))
最初にツリーを展開するときに常にドロワをたたむ
ツリーが展開されて表示されるのが気に食わないので,畳んだ状態でツリー内部を表示するように矯正します.
;; プロパティ等を自動的閉じる.
(defun my-org-hide-drawers ()
  "Hide all drawers in an org tree."
  (interactive)
  (save-excursion
    (beginning-of-line)
    (unless (looking-at-p org-drawer-regexp)
      (org-cycle-hide-drawers 'subtree))))
(add-hook 'org-tab-first-hook 'my-org-hide-drawers)
orgmode のテーブルを csv に変換する(さらにクリップボードに流す)
標準関数の org-table-export を使えば,テーブルを csv 等の形式で外部出力できます.ただバッファ上で csv に変えたりする関数があると楽なので次の関数を使います.標準関数の一部を再利用しています.
M-x org-table-to-format を呼び出すとデフォルトでカンマ区切りに変換してくれます.さらにその情報をコピペできるようにクリップボードに流し込みます.
(defun my-org-table-copy-as (&optional format)
  "Copy converted table."
  (interactive)
  (let ((format (or format
                    (org-entry-get (point) "TABLE_EXPORT_FORMAT" t)
                    org-table-export-default-format)))
    (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format)
        (let ((transform (intern (match-string 1 format)))
              (params (and (match-end 2)
                           (read (concat "(" (match-string 2 format) ")"))))
              (table (org-table-to-lisp)))
          (if (not (org-at-table-p))
              (user-error "The cursor is not at a table")
            (with-temp-buffer
              (insert (funcall transform table params) "\n")
              (clipboard-kill-ring-save (point-min) (point-max)))))
      (user-error "TABLE_EXPORT_FORMAT invalid"))))
(defun my-org-table-convert-to (&optional format)
  "Convert a table to FORMAT.
If FORMAT is nil, it is set equal to a property value specified
by \"TABLE_EXPORT_FORMAT\" or `org-table-export-default-format'.
Converted table is copied to kill ring for further use.
The core part is extracted from `org-table-export'."
  (interactive)
  (let ((format (or format
                    (org-entry-get (point) "TABLE_EXPORT_FORMAT" t)
                    org-table-export-default-format)))
    (if (string-match "\\([^ \t\r\n]+\\)\\( +.*\\)?" format)
        (let ((transform (intern (match-string 1 format)))
              (params (and (match-end 2)
                           (read (concat "(" (match-string 2 format) ")"))))
              (table (org-table-to-lisp)))
          (if (not (org-at-table-p))
              (user-error "The cursor is not at a table")
            (kill-region (org-table-begin) (org-table-end))
            (let ((begin (point)))
              (insert (funcall transform table params))
              (clipboard-kill-ring-save begin (point))
              (insert "\n"))))
      (user-error "TABLE_EXPORT_FORMAT invalid"))))
ソースブロックの配色
org-src-block-faces, org-block-begin-line, org-block-end-line をカスタマイズして,配色を変えます.

さらに, prettify-symbols-mode を利用して begin_src と end_src の表示を別な文字列に変えます(次節参照).
(with-eval-after-load "org"
  (defun my-org-src-block-face ()
    (setq org-src-block-faces
          (if (eq 'light (frame-parameter nil 'background-mode))
              '(("emacs-lisp" (:background "#F9F9F9" :extend t))
                ("conf" (:background "#F9F9F9" :extend t))
                ("org" (:background "#F9F9F9" :extend t))
                ("html" (:background "#F9F9F9" :extend t)))
            '(("emacs-lisp" (:background "#383c4c" :extend t))
              ("conf" (:background "#383c4c" :extend t))
              ("org" (:background "#383c4c" :extend t))
              ("html" (:background "#383c4c" :extend t)))))
    (font-lock-fontify-buffer))
  (add-hook 'ah-after-enable-theme-hook #'my-org-src-block-face)
  (my-org-src-block-face)
  (custom-set-faces
   ;; org-block が効かない(2021-04-13@9.4.4),org-src-block-faces で対応
   ;; '(org-block
   ;;   ((((background dark)) (:background "#383c4c" :extend t)
   ;;     (t (:background "#F9F9F9" :extend t)))))
   '(org-block-begin-line
     ((((background dark))
       (:foreground "#669966" :weight bold)) ;; :background "#444444"
      (t (:foreground "#CC3333" :weight bold)))) ;; :background "#EFEFEF"
   '(org-block-end-line
     ((((background dark)) (:foreground "#CC3333" :weight bold))
      (t (:foreground "#669966" :weight bold))))
   ;; '(org-block-end-line
   ;;   ((((background dark)) (:inherit org-block-begin-line))
   ;;    (t (:inherit org-block-begin-line))))
   ))
orgmode のリストのチェックボックスをアイコンに変えて見やすくします
チェックボックスをアイコン化しました.さらに,ソースブロックもわかりやすいアイコンと配色に置き換えました. icons-in-terminal に依存しています(all-the-icons でも同じことができるはずです)


(with-eval-after-load "icons-in-terminal"
  (setq-default prettify-symbols-alist '(;;("#+begin_src" . "")
                                         ("#+begin_src" . "▨")
                                         ("#+end_src" . "▨")
                                         ("#+RESULTS:" . "")
                                         ("[ ]" .  "") ;; ☐ 
                                         ("[X]" . "" ) ;; ☑ 
                                         ("[-]" . "" ))) ;; 
  (add-hook 'org-mode-hook 'prettify-symbols-mode))
(with-eval-after-load "org"
  (custom-set-faces
   '(org-block-begin-line
     ((((background dark))
       (:foreground "#669966" :weight bold)) ;; :background "#444444"
      (t (:foreground "#CC3333" :weight bold)))) ;; :background "#EFEFEF"
   '(org-block-end-line
     ((((background dark)) (:foreground "#CC3333" :weight bold))
      (t (:foreground "#669966" :weight bold))))
   ))
orgバッファをPGPで暗号化したツリーを自分宛てに送る
M-x my-insert-enc2me-pgp-tree でカーソル位置に新しいツリーを挿入します.あるツリーの中身を,公開鍵を持った端末で暗号化して,ツリーの内容を秘密鍵のある端末にメールすることを想定しています.暗号化自体は,ツリーのプロパティに CRYPTKEY が設定されていて,その公開鍵を保持していれば, M-x org-encrypt-entry で実行するだけです.
(defun my-insert-enc2me-pgp-tree ()
  (interactive)
  (insert "** TODO share with me\n")
  (insert "   :PROPERTIES:\n")
  (insert "   :CRYPTKEY: takaxp@ieee.org\n")
  (insert "   :END:\n")
  (insert "\n")
  (forward-line -1))
View mode 関連
ChangeLog で org syntax を使いつつ基本はViewモードで閲覧する
ChangeLog で orgalist-mode を有効にして,バッファ訪問時に view mode を有効にしています.同バッファで C-x 4 a したときには,自動で view mode を無効にしています.
(defun my-orgalist-activate ()
  (when (require 'orgalist nil t)
    (orgalist-mode 1))) ;; originally orgstruct-mode
(add-hook 'change-log-mode-hook
          (lambda ()
            (view-mode 1)
            (my-orgalist-activate)
            (setq tab-width 4)
            (setq left-margin 4)))
(defun ad:add-change-log-entry-other-window ()
  (when view-mode
    (View-exit-and-edit)))
(advice-add 'add-change-log-entry-other-window
            :before #'ad:add-change-log-entry-other-window)
基本的に view mode で訪問したいバッファやディレクトリを指定
まず,個別のファイルで view モードで開くことを指定するには次のようにします.
-*- mode:org; eval: (view-mode) -*-
一方,特定の拡張子に対して常に view モードで開きたい,例えば,gzされた elisp ソースを見るときに, view-mode を使います.また下記の設定では, my-auto-view-dirs に追加したディレクトリのファイルを開くと, view-mode が常に有効になります.
さらなる細かい制御が必要な場合は,viewer.el がおすすめです.
view-mode は独自のキーバインドを設定されているので,カーソル移動や <tab> を好みの状態に変えることで,より違和感なく使えるようになります. org バッファにおける n や <tab> などの振る舞いですね.また origami.el があれば,~org~ バッファ以外でもFOLD機能を使って関数を簡略表示して,注目するもの(関数)だけを読むことが可能です.
;; 特定の拡張子・ディレクトリ
(defvar my-auto-view-regexp "\\.el.gz$\\|\\.patch$\\|\\.xml$\\|\\.csv$\\|\\.emacs.d/[^/]+/el-get\\|config")
;; 特定のディレクトリ(絶対パス・ホームディレクトリ以下)
(defvar my-auto-view-dirs nil)
(add-to-list 'my-auto-view-dirs "~/devel/emacs-head/emacs/")
(add-to-list 'my-auto-view-dirs "~/devel/git/org-mode/lisp/")
(when (eq window-system 'w32)
  (add-to-list 'my-auto-view-dirs "c:/msys64/mingw64"))
;;;###autoload
(defun my-auto-view ()
  "Open a file with `view-mode'."
  (when (and my-auto-view-regexp
             (string-match my-auto-view-regexp buffer-file-name))
    (view-mode 1))
  (dolist (dir my-auto-view-dirs)
    (when (eq 0 (string-match (expand-file-name dir) buffer-file-name))
      (view-mode 1))))
(add-hook 'find-file-hook #'my-auto-view)
;;;###autoload
(defun my-org-view-next-heading ()
  (interactive)
  (if (and (derived-mode-p 'org-mode)
           (org-at-heading-p))
      (org-next-visible-heading 1)
    (next-line)))
;;;###autoload
(defun my-org-view-previous-heading ()
  (interactive)
  (if (and (derived-mode-p 'org-mode)
           (org-at-heading-p))
      (org-previous-visible-heading 1)
    (previous-line)))
;;;###autoload
(defun my-view-tab ()
  (interactive)
  (if (and (derived-mode-p 'org-mode)
           (or (org-at-heading-p)
               (org-at-property-drawer-p)))
      (let ((view-mode nil))
        (org-cycle))
    (when (require 'origami nil t)
      (origami-toggle-node (current-buffer) (point)))))
;;;###autoload
(defun my-view-shifttab ()
  (interactive)
  (if (derived-mode-p 'org-mode)
      (let ((view-mode nil))
        (org-shifttab))
    (when (require 'origami nil t)
      (origami-toggle-all-nodes (current-buffer)))))
;;;###autoload
(defun my-unlock-view-mode ()
  (when view-mode
    (View-exit-and-edit)))
(with-eval-after-load "view"
  (define-key view-mode-map (kbd "i") 'View-exit-and-edit)
  (define-key view-mode-map (kbd "<SPC>") 'ignore)
  (define-key view-mode-map (kbd "<DEL>") 'ignore)
  (define-key view-mode-map (kbd "S-SPC") 'mac-ime-toggle)
  (define-key view-mode-map (kbd "f") 'forward-char)
  (define-key view-mode-map (kbd "b") 'backward-char)
  (define-key view-mode-map (kbd "n") 'my-org-view-next-heading)
  (define-key view-mode-map (kbd "p") 'my-org-view-previous-heading)
  (define-key view-mode-map (kbd "g") #'my-google-this)
  (define-key view-mode-map (kbd "<tab>") 'my-view-tab)
  (define-key view-mode-map (kbd "S-<tab>") 'my-view-shifttab)
  (defun ad:view--enable () (my-mode-line-on))
  (defun ad:view--disable () (my-mode-line-off))
  (unless my-toggle-modeline-global
    (advice-add 'view--enable :before #'ad:view--enable)
    (advice-add 'view--disable :before #'ad:view--disable)))
既存設定の改善
focus-in-hook と focus-out-hook を使わないようにする
focus-in-hook と focus-out-hook は,27.1からobsoleteになっていたそうで,代わりに after-focus-change-function を使えば良いようです.
;; FIXME
;;(add-hook 'focus-in-hook (lambda () (setq ox-icalendar-activate nil)))
;;(add-hook 'focus-out-hook (lambda () (setq ox-icalendar-activate t)))))
(add-function :after after-focus-change-function #'my-ox-icalendar-activate)
なお,今フォーカスがあたっているのか?を知りたい場合は, (frame-focus-state) を使って判定できます.
counsel-ag 等で2文字以上での探索語を指定できるようにする
デフォルトでは3文字以上の単語を検索できるように指定されていますが,意外と2文字でも探索したいケースが多いので,2文字も受け付けるようにしました.
(add-to-list 'ivy-more-chars-alist '(counsel-ag . 2))
マークリングをたどる
C-, に counsel-mark-ring をアサインしました.
(define-key org-mode-map (kbd "C-,") 'counsel-mark-ring)
句読点の一括変換
私は環境の違いで「,.」を使ったり,「、。」を使ったりしますが,両者を相互に変換できるようにしました. M-x my-replace-punctuation-to-normal で「、。」に統一, M-x my-replace-punctuation-to-scientific で「,.」に統一できます.
;;;###autoload
(defun my-replace-punctuation-to-normal ()
  (interactive)
  (my-replace-punctuation 'normal))
;;;###autoload
(defun my-replace-punctuation-to-scientific ()
  (interactive)
  (my-replace-punctuation 'scientific))
(defun my-replace-punctuation (to)
  (let ((pos (point))
        (source (cond ((eq to 'normal) "\\(,\\)\\|\\(.\\)")
                      ((eq to 'scientific) "\\(、\\)\\|\\(。\\)"))))
    (if (not source)
        (error "Target punctuation is wrong")
      (goto-char (point-min))
      (while (re-search-forward source nil :noerror)
        (let ((w (match-string-no-properties 0)))
          (cond ((equal w ",") (replace-match "、"))
                ((equal w ".") (replace-match "。"))
                ((equal w "、") (replace-match ","))
                ((equal w "。") (replace-match ".")))))
      (goto-char pos))))
dired で上位ディレクトリに移動するキーバインドを設定
u 押下一発で移動できるのが楽ですね.ターミナルで普段使うシェルでも同じ設定をしていて,移植しました.
(define-key dired-mode-map (kbd "u") 'dired-up-directory)
ビジュアライズ
Emacs が GUIアプリとして生き残るにはとにかく「見た目がすべて!」と個人的には信じているので,少しでも ダサい 古めかしい見た目から脱却して,VSCにも負けないようなものに保ちたいところです.
icons-in-terminal で積極的にモードラインをカスタマイズ
表示中のバッファの種類に合わせてアイコンを表示
mode-line-mule-info 周りの設定は,モードラインの文字エンコーディング表示をわかりやすくする - Qiitaを取り入れています.
(make-face 'mode-line-file-icon-face)
(custom-set-faces
 '(mode-line-file-icon-face
   ((((background dark)) :foreground "VioletRed1")
    (t (:foreground "LightGoldenrod1")))))
(defun my-mode-line-icon-for-file ()
  (icons-in-terminal-icon-for-file
   (buffer-name) :v-adjust 0.03 :face 'mode-line-file-icon-face))
(defun my-buffer-coding-system-mnemonic ()
  "Return a mnemonic for `buffer-file-coding-system'."
  (let* ((code buffer-file-coding-system)
         (name (my-coding-system-name-mnemonic code))
         (bom (my-coding-system-bom-mnemonic code)))
    (format "%s %s%s" (my-mode-line-icon-for-file) name bom)))
;; `mode-line-mule-info' の文字エンコーディングの文字列表現を差し替える
(setq-default mode-line-mule-info
              (cl-substitute '(:eval (my-buffer-coding-system-mnemonic))
                             "%z" mode-line-mule-info :test 'equal))
View mode のとき,鍵マークをモードラインに表示する
(defun my-mode-line-icon-lock ()
  (if view-mode
      (concat (icons-in-terminal-faicon
               "lock" :face '(:foreground "#FF0000")) " ") ""))
モードラインの行数表示の前にアイコンを追加
(with-eval-after-load "icons-in-terminal"
  (setq mode-line-position-line-format
        `(,(icons-in-terminal-material "edit") "%3l")))
Gitリポジトリの変更有無をアイコンで表示
(with-eval-after-load "icons-in-terminal"
  ;; 変更がアリ時は赤アイコン,そうでない時に緑アイコンをモードラインに表示
  (make-face 'mode-line-vc-normal-face)
  (make-face 'mode-line-vc-modified-face)
  (set-face-attribute 'mode-line-vc-normal-face nil :foreground "#AFFFAF")
  (set-face-attribute 'mode-line-vc-modified-face nil :foreground "#EEAFAF")
  (defun my-mode-line-vc-mode-icon ()
    (if (string-match "^ Git:" vc-mode)
        (replace-regexp-in-string
         "^ Git:" (propertize " " 'face 'mode-line-vc-modified-face) vc-mode)
      (replace-regexp-in-string
       "^ Git-" (propertize " " 'face 'mode-line-vc-normal-face) vc-mode)))
  (setcdr (assq 'vc-mode mode-line-format)
          '((:eval (my-mode-line-vc-mode-icon)))))
ターミナル時のウィンドウ分割線を見やすくする
see https://www.reddit.com/r/emacs/comments/3u0d0u/how_do_i_make_the_vertical_window_divider_more/

(unless (display-graphic-p)
  ;; ターミナルの縦分割線をUTF-8できれいに描く
  (defun my-change-window-divider ()
    (interactive)
    (let ((display-table (or buffer-display-table
           standard-display-table
           (make-display-table))))
      (set-display-table-slot display-table 5 ?│)
      (set-window-display-table (selected-window) display-table)))
  (add-hook 'window-configuration-change-hook 'my-change-window-divider))
display-line-numbers の表示桁数を5桁以上にする
通常はバッファの行数に応じて桁数が変わりますが,これを5桁以上に強制します.さらに,display-line-numbers が有効な時は,モードラインから行数表示を消します.
(when (require 'display-line-numbers nil t)
  (global-set-key (kbd "C-<f12>") 'my-toggle-display-line-numbers-mode)
  (with-eval-after-load "display-line-numbers"
    (custom-set-variables
     '(display-line-numbers-width-start t))
    ;; ウィンドウ左に表示する行数の幅を5以上に固定する.
    (defun my-display-line-numbers-width ()
      (when (< display-line-numbers-width 5)
        (setq display-line-numbers-width 5))
      (setq moom-display-line-numbers-width (+ 2 display-line-numbers-width)))
    (add-hook 'display-line-numbers-mode-hook #'my-display-line-numbers-width)
    (defun my-display-line-numbers-mode-on ()
      "Trun on `display-line-numbers'."
      (interactive)
      (if (fboundp 'global-display-line-numbers-mode) ;; 26.1 or later
          (unless global-display-line-numbers-mode
            (global-display-line-numbers-mode 1)
            (line-number-mode -1))
        (user-error "The display-line-numbers is NOT supported")))
    (defun my-display-line-numbers-mode-off ()
      "Trun off `display-line-numbers'."
      (interactive)
      (if (fboundp 'global-display-line-numbers-mode) ;; 26.1 or later
          (when global-display-line-numbers-mode
            (global-display-line-numbers-mode -1)
            (line-number-mode 1))
        (user-error "The display-line-numbers is NOT supported")))
    (defun my-toggle-display-line-numbers-mode ()
      "Toggle variable `global-display-line-numbers-mode'."
      (interactive)
      (if (fboundp 'global-display-line-numbers-mode) ;; 26.1 or later
          (let ((flag (if global-display-line-numbers-mode -1 1)))
            (global-display-line-numbers-mode flag)
            (line-number-mode (- flag)))
        (user-error "The display-line-numbers is NOT supported")))))
org-tree-slide.el で一時的に #+ATTR_ORG などを非表示にする
問い合わせが来るまで org-tree-slide.el が Doom Emacs の一部に組み込まれていることを知らなかったのですが,チケット解消の過程で思いついたので,以下の設定を取り込みました.
プレゼンテーション実行時に,制御コードが記述されている #+ATTR_ORG などの行を非表示(背景と同じ色)にします.
(defvar my-hide-org-meta-line-p nil)
(defun my-hide-org-meta-line ()
  (interactive)
  (setq my-hide-org-meta-line-p t)
  (set-face-attribute 'org-meta-line nil
                      :foreground (face-attribute 'default :background)))
(defun my-show-org-meta-line ()
  (interactive)
  (setq my-hide-org-meta-line-p nil)
  (set-face-attribute 'org-meta-line nil :foreground nil))
(defun my-toggle-org-meta-line ()
  (interactive)
  (if my-hide-org-meta-line-p
      (my-show-org-meta-line) (my-hide-org-meta-line)))
(add-hook 'org-tree-slide-play-hook #'my-hide-org-meta-line)
(add-hook 'org-tree-slide-stop-hook #'my-show-org-meta-line)
;; Option
(defun my-update-org-meta-line ()
  (interactive)
  (when my-hide-org-meta-line-p
    (my-hide-org-meta-line)))
(add-hook 'ah-after-enable-theme-hook #'my-update-org-meta-line)
フリンジに表示する折返しマークを変更
色々とカスタマイズしているうちに,どうもフリンジに表示される折返しマークが気に入らなくなりカスタマイズしてみました.右側のフリンジにだけ表示するようにしています
ただ実際のところ,まだアイコン的にはしっくりきていません.
(setq truncate-lines nil)
(setq truncate-partial-width-windows nil)
(setq-default fringe-indicator-alist
              (append (list '(continuation . (nil right-curly-arrow)))
                      (remove (assoc 'continuation fringe-indicator-alist)
                              fringe-indicator-alist)))
;; fringeに表示するマークの形状を変更
(define-fringe-bitmap 'right-curly-arrow
  [#b00000000
   #b00000000
   #b00000000
   #b00000000
   #b01111110
   #b01111110
   #b00000110
   #b00000110])
Emacs 28関連
Native Comp が使えるかを確認する
Emacs 28から,ビルド時にネイティブコンパイルが可能になりました.ただそうでないEmacsもまだ使っているので,設定を切り分ける目的で,ネイティブコンパイルされたEmacsを判別できるようにしました.
(defun my-native-comp-p ()
  (when (fboundp 'native-comp-available-p)
    (native-comp-available-p)))
Bookmark 周りに Face の変更
純粋に気に食わなかったので変更しました.
;; ビルトイン bookmark の配色を無効にする(as of 28.1)
(setq bookmark-fontify nil)
;; ビルトイン bookmark がfringeに出すマークを無効にする(as of 28.1)
(setq bookmark-set-fringe-mark nil)
おわりに
2021年はそれほど設定を更新した印象がなかったのですが,いざ diff を取って確認してみると,細かいところでたくさん変更していることがわかりました.中には,アルファ版のリリースが開始された次期メジャーバージョン Emacs 28 に対応するための設定も入っていて,季節の変わり目を感じました.その点では,27の寿命が短かったような…
2022年は自作パッケージに届いている改良リクエストなどに答えていけたら良いなと思う年の瀬です.





