【Emacs】centaur-tabs-mode で黒系のアイコンがdark系カラーテーマのときに見えづらくなる(nerd-icon)

centaur-tabs-mode で黒系のアイコンの例は ".emacs" の nf-oct-gear だが、これはdark系カラーテーマ(例えばdoom-dracula)のときには見えづらい。 ほか、term-modeの nf-dev-terminal などもそうであり、結局これらにはfaceが付与されていないのである (nerd-icons.el)。

そこで centaur-tabs-icon を再定義した。

(defun centaur-tabs-icon (tab face selected)
  "Generate icon for TAB using FACE's background.
If icon gray out option enabled, gray out icon if not SELECTED."
  (if centaur-tabs-icon-type
      (with-current-buffer (car tab)
        (let* ((icon
                (or (ignore-errors
                      (centaur-tabs--icon-for-file
                       (file-name-nondirectory (buffer-file-name))
                       :v-adjust centaur-tabs-icon-v-adjust
                       :height centaur-tabs-icon-scale-factor))
                    (ignore-errors
                      (centaur-tabs--icon-for-mode
                       major-mode
                       :v-adjust centaur-tabs-icon-v-adjust
                       :height centaur-tabs-icon-scale-factor))))
               (background (face-background face nil 'default))
               (inactive (cond ((and (not selected)
                                     (eq centaur-tabs-gray-out-icons 'buffer))
                                (face-foreground 'mode-line-inactive nil 'default))
                               ((or centaur-tabs-plain-icons
                                    (ignore-errors ;; for .emacs
                                      (eq (string-match "^\\." (file-name-nondirectory (buffer-file-name))) 0))
                                    (derived-mode-p 'term-mode))
                                (face-foreground 'centaur-tabs-selected nil 'default))
                               (t 'unspecified)))
               (underline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'under)
                               (face-attribute face :underline)))
               (overline (and (eq (if (display-graphic-p) centaur-tabs-set-bar) 'over)
                              (face-attribute face :overline))))
          (if (stringp icon)
              (progn
                (propertize icon 'face `(:inherit ,(get-text-property 0 'face icon)
                                                  :foreground ,inactive
                                                  :background ,background
                                                  :underline ,underline
                                                  :overline ,overline)))
            "")))
    ""))

【Emacs】tab-bar-mode のタブ削除・新規タブのボタンが小さいので大きくした / How to make the tab deletion and new tab buttons in tab-bar-mode enlarged

こうする.define-iconで調整する.修正の本質は

:height (1.0 . em)

を追加することにあり,この1.0を変えれば大きさも変化する.タブ履歴のほうは heightを0.9にしてみた.

ただしこのときのEmacsのバージョンは29.4である.

(with-eval-after-load 'tab-bar

  (setq tab-bar-new-button-show t)  ;; nil: 新規タブボタンは非表示
  (setq tab-bar-close-button-show t) ;; nil:タブ削除ボタンは非表示

  (setq tab-bar-new-tab-to 'right) ;; 新規タブは一番右

  ;; 各ボタンの有無はお好みで
  (setq tab-bar-format '(tab-bar-format-history
                         tab-bar-format-tabs
                         tab-bar-separator tab-bar-format-add-tab))

  (setq tab-bar-history-limit 100) ;; ヒストリの最大履歴

  (require 'icons) ;; define-icon をロードするため
  
  ;; 新規タブマークが小さすぎるので大きくする
  (define-icon tab-bar-new nil
    `((image "tabs/new.xpm"
             :height (1.0 . em)
             :margin ,tab-bar-button-margin
             :ascent center)
      (text " + "))
    "Icon for creating a new tab."
    :version "29.1"
    :help-echo "New tab")

  ;; タブ削除マークが小さすぎるので大きくする
  (define-icon tab-bar-close nil
    `((image "tabs/close.xpm"
             :height (1.0 . em)
             :margin ,tab-bar-button-margin
             :ascent center)
      (text " x"))
    "Icon for closing the clicked tab."
    :version "29.1"
    :help-echo "Click to close tab")

  ;; タブの履歴マークも小さすぎるのでついでに大きくする
  (define-icon tab-bar-back nil
    `((image "tabs/left-arrow.xpm"
             :height (0.9 . em)
             :margin ,tab-bar-button-margin
             :ascent center)
      (text " < "))
    "Icon for going back in tab history."
    :version "29.1")
  (define-icon tab-bar-forward nil
    `((image "tabs/right-arrow.xpm"
             :height (0.9 . em)
             :margin ,tab-bar-button-margin
             :ascent center)
      (text " > "))
    "Icon for going forward in tab history."
    :version "29.1")

  (setq tab-bar-history-limit 100) ;; ヒストリの最大履歴

  (tab-bar-mode 1)
  (tab-bar-history-mode 1) ;; 各タブについて,ウィンドウ構成のヒストリを記憶する
  )

Emacs開発者のブランチにはボタン拡大のコミットがされており,Emacs 30 系では反映されていると思われる.

git.savannah.gnu.org

tab-bar-mode についてはこちらの記事も参考に.

tam5917.hatenablog.com

【Emacs】sticky scroll 相当の挙動を実現するには? / How to achieve sticky scroll behavior in Emacs?

VSCodeではsticky scroll が有効になっている。

Emacsで「ほぼ」同様の機能を実現するには

のが一番良さそうである。設定はこんな感じ(主にpython向け):

(add-hook 'emacs-startup-hook #'(lambda ()
                                  (add-hook 'prog-mode-hook #'window-stool-mode)
                                  (setq window-stool-n-from-top 2)
                                  (setq window-stool-n-from-bottom 0)
                                  ))

類似のパッケージは

  • topsyをインストールし、topsy-mode を有効にする qiita.com

もしくは

  • breadcrumb-mode をインストールし、有効にする

  • scroll-conservativelyを1に設定する

ちなみにbreadcrumb-modeの設定:

(breadcrumb-mode +1)

;; 画面の下端で「↓向きに1行移動」したり,上端で「↑向きに1行移動」したりしたときの自動スクロール量を1に設定する.
;; デフォルトは0(無効値)で,半画面分(ウィンドウ高さの50%)自動スクロールする動作になる。
;; 厳密に説明すると、ここで指定した行数だけ移動すると(goto-lineなどの長距離移動を含む)、リセンタリングが発生する。
;; 1に設定すれば、C-nやC-pでの移動ではリセンタリングは発生しないというわけである。
(setq scroll-conservatively 1)

;; また十分大きな値(> 100)にすることで、
;; goto-lineなどによるカーソルの長距離移動を含めてリセンタリングを事実上OFFにできる。
;; どちらが良いかはお好みで。
;; (setq scroll-conservatively 101)

topsyとbreadcrumb-mode、いずれも頭出しの関数名までが表示される。後者は引数を含めた情報までスクロールされない。ゆえに「ほぼ同様」のsticky scroll もどき、といったわけである。

カーソルに追従するスクロールの挙動をVSCodeにさらに近づけてみたい場合は以下の設定を試してみるのも良い。

;; スクロールのマージン
;; 画面スクロールにあたり、ウィンドウの高さがこの行数だけ小さくなるとみなす
(setq scroll-margin 3)

スクロールの挙動/設定について解説した記事は以下:

exlight.net

scroll-marginについて説明した記事は以下:

yohshiy.blog.fc2.com

【Emacs】dired-modeでGNU sushiを使ったファイルプレビュー機能の実装 / Implementaion of file preview function using GNU sushi in dired-mode

こうする. call-process-shell-command の活用がカギだった.

(with-eval-after-load "dired"
  (defvar dired-sushi-search-command
    "xdotool search --desktop 0 org.gnome.NautilusPreviewer windowactivate")
  (defun dired-sushi ()
    (interactive)
    (call-process-shell-command 
     (format "sushi %s; sleep 0.1; %s" (dired-get-file-for-visit)
             dired-sushi-search-command)
     nil 0))
  (define-key dired-mode-map (kbd "SPC") #'dired-sushi)
  )

【Emacs】neotreeで使うiconをall-the-iconsからnerd-iconsに変えた

こんな感じで neo-buffer--insert-fold-symbol を再定義した.nerd-icons-icon-for-dir-with-chevron は新規に作成した.

(defun neo-buffer--insert-fold-symbol (name &optional node-name)
  "Write icon by NAME, the icon style affected by neo-theme.
`open' write opened folder icon.
`close' write closed folder icon.
`leaf' write leaf icon.
Optional NODE-NAME is used for the `icons' theme"
  (let ((n-insert-image (lambda (n)
                          (insert-image (neo-buffer--get-icon n))))
        (n-insert-symbol (lambda (n)
                           (neo-buffer--insert-with-face
                            n 'neo-expand-btn-face))))
    (cond
     ((and (display-graphic-p) (equal neo-theme 'classic))
      (or (and (equal name 'open)  (funcall n-insert-image "open"))
          (and (equal name 'close) (funcall n-insert-image "close"))
          (and (equal name 'leaf)  (funcall n-insert-image "leaf"))))
     ((equal neo-theme 'arrow)
      (or (and (equal name 'open)  (funcall n-insert-symbol "▾"))
          (and (equal name 'close) (funcall n-insert-symbol "▸"))))
     ((equal neo-theme 'nerd)
      (or (and (equal name 'open)  (funcall n-insert-symbol "▾ "))
          (and (equal name 'close) (funcall n-insert-symbol "▸ "))
          (and (equal name 'leaf)  (funcall n-insert-symbol "  "))))
     ((and (display-graphic-p) (equal neo-theme 'icons))
      (unless (require 'all-the-icons nil 'noerror)
        (error "Package `all-the-icons' isn't installed"))
      (setq-local tab-width 1)
      (or (and (equal name 'open)  (insert (all-the-icons-icon-for-dir-with-chevron (directory-file-name node-name) "down")))
          (and (equal name 'close) (insert (all-the-icons-icon-for-dir-with-chevron (directory-file-name node-name) "right")))
          (and (equal name 'leaf)  (insert (format "\t\t\t%s\t" (all-the-icons-icon-for-file node-name))))))
     ((and (display-graphic-p) (equal neo-theme 'nerd-icons))
      (unless (require 'nerd-icons nil 'noerror)
        (error "Package `nerd-icons' isn't installed"))
      (setq-local tab-width 1)
      (or (and (equal name 'open)  (insert (nerd-icons-icon-for-dir-with-chevron (directory-file-name node-name) "down")))
          (and (equal name 'close) (insert (nerd-icons-icon-for-dir-with-chevron (directory-file-name node-name) "right")))
          (and (equal name 'leaf)  (insert (format "\t\t\t\t%s\t" (nerd-icons-icon-for-file node-name))))))
     (t
      (or (and (equal name 'open)  (funcall n-insert-symbol "- "))
          (and (equal name 'close) (funcall n-insert-symbol "+ ")))))))

(defun nerd-icons-icon-for-dir-with-chevron (dir &optional chevron padding)
  "Format an icon for DIR with CHEVRON similar to tree based directories.

If PADDING is provided, it will prepend and separate the chevron
and directory with PADDING.

Produces different symbols by inspecting DIR to distinguish
symlinks and git repositories which do not depend on the
directory contents"
  (let ((icon (nerd-icons-icon-for-dir dir))
        (chevron (if chevron (nerd-icons-octicon (format "nf-oct-chevron_%s" chevron) :height 0.8 :v-adjust -0.1) ""))
        (padding (or padding "\t")))
    (format "%s%s%s%s%s" padding chevron padding icon padding)))

あとは

(setq neo-theme 'nerd-icons)

としておけばOK. 当然,nerd-icons はインストールしておくこと.

余談

nerd fontsの検索に便利なサイト

www.nerdfonts.com

追記

今回の記事の後にほぼ同じ修正を施したプルリクを見つけた。

github.com

追記2

上記プルリクはその後、マージされた。

【Emacs】completion-preview の設定

ちなみに completion-previewEmacs 30 系に搭載予定.

最新のソースコードはこちらから。

github.com

(require 'completion-preview)

;; Enable Completion Preview mode in code buffers
(add-hook 'prog-mode-hook #'completion-preview-mode)
;; also in text buffers
(add-hook 'text-mode-hook #'completion-preview-mode)
;; also in eshell-buffers
(add-hook 'eshell-mode-hook #'completion-preview-mode)

;; and in \\[shell] and friends
(with-eval-after-load 'comint
  (add-hook 'comint-mode-hook #'completion-preview-mode))

(setq completion-preview-minimum-symbol-length 3)

;; Non-standard commands to that should show the preview:

;; Org mode has a custom `self-insert-command'
(push 'org-self-insert-command completion-preview-commands)
;; Paredit has a custom `delete-backward-char' command
(push 'paredit-backward-delete completion-preview-commands)

;; Bindings that take effect when the preview is shown:

;; Cycle the completion candidate that the preview shows
(keymap-set completion-preview-active-mode-map
            "M-n" #'completion-preview-next-candidate)
(keymap-set completion-preview-active-mode-map
            "M-p" #'completion-preview-prev-candidate)

;; Convenient alternative to C-i after typing one of the above
;; (keymap-set completion-preview-active-mode-map
;;             "M-i" #'completion-preview-insert)

(keymap-set completion-preview-active-mode-map
            "M-i" #'completion-preview-complete)

(keymap-set completion-preview-active-mode-map
            "<remap> <forward-word>" #'completion-preview-insert-word)
(keymap-set completion-preview-active-mode-map
            "<remap> <forward-sexp>" #'completion-preview-insert-sexp)

参考

eshelyaron.com

【Emacs】centaur-tabs の設定

こんな感じ.

;; 'tab タブ切り替えの単位をグループ内の「バッファ」に限定
;; 'groups タブ切り替えの単位を「グループ」に限定
(setq centaur-tabs-cycle-scope 'tabs)

;; アイコン表示
(setq centaur-tabs-set-icons t)
(setq centaur-tabs-icon-type 'all-the-icons)

;; バッファ名の代わりにグループ表示する場合
;; (setq centaur-tabs--buffer-show-groups nil)
;; (centaur-tabs-toggle-groups)

;; t: 変更され未保存のバッファにマーカーを表示する
(setq centaur-tabs-set-modified-marker t)
(setq centaur-tabs-modified-marker "●")

;; 'over: 現在選択中のタブに上線を引く
(setq centaur-tabs-set-bar 'over)

;; キーバインド
(global-set-key (kbd "C-<tab>") 'centaur-tabs-forward)
(global-set-key (kbd "C-S-<iso-lefttab>") 'centaur-tabs-backward)
(global-set-key (kbd "C-z C-s") #'centaur-tabs-switch-group)

;; 特有の関数は centaur-tabs-mode を有効にしたあとで定義する
(centaur-tabs-mode t)

;; タブを非表示にしたいバッファ
(defun centaur-tabs-hide-tab (x)
  "Do no to show buffer X in tabs."
  (let ((name (format "%s" x)))
    (or
     ;; Current window is not dedicated window.
     (window-dedicated-p (selected-window))

     ;; Buffer name not match below blacklist.
     (string-prefix-p "*epc" name)
     (string-prefix-p "*lsp" name)
     (string-prefix-p "*Flycheck" name)
     (string-prefix-p "*tramp" name)
     (string-prefix-p " *Mini" name)
     (string-prefix-p "*help" name)
     (string-prefix-p " *temp" name)
     (string-prefix-p "*Help" name)
     (string-prefix-p "*Compile-Log*" name)
     ;; (string-prefix-p "*scratch*" name)
     (string-prefix-p "*Messages*" name)
     (string-prefix-p "*Async-native-compile-log*" name)
     (string-prefix-p "*Native-compile-Log" name))))

(defun which-active-modes ()
  "Return which minor modes are enabled in the current buffer."
  (interactive)
  (let ((active-modes))
    (mapc (lambda (mode) (condition-case nil
                             (if (and (symbolp mode) (symbol-value mode))
                                 (add-to-list 'active-modes mode))
                           (error nil) ))
          minor-mode-list)
    active-modes))

;; バッファのメジャーモードに沿ってグループ化
(defun centaur-tabs-buffer-groups ()
  "`centaur-tabs-buffer-groups' control buffers' group rules."
  (list
   (cond
    ((derived-mode-p 'shell-mode) "Shell")
    ((derived-mode-p 'eshell-mode) "EShell")
    ((derived-mode-p 'emacs-lisp-mode) "Elisp")
    ((derived-mode-p 'dired-mode) "Dired")
    ((derived-mode-p 'python-ts-mode) "Python")
    ((derived-mode-p 'c-mode) "C-lang")
    ((derived-mode-p 'LaTeX-mode 'TeX-mode) "LaTeX")
    ((or (derived-mode-p 'howm-menu-mode)
         (memq 'howm-mode (which-active-modes))) "HOMW")
    ((derived-mode-p 'yaml-ts-mode 'conf-toml-mode) "Config")
    ((derived-mode-p 'org-mode 'org-agenda-mode 'diary-mode) "OrgMode")
    ((string-equal "*" (substring (buffer-name) 0 1)) "Emacs")
    (t
     (centaur-tabs-get-group-name (current-buffer))))))

;; タブを閉じるためのユーティリティ
(defun centaur-close-ace-jump (&optional arg)
  (interactive)
  (centaur-tabs-ace-action 'close-tab))