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

【Emacs】補完機能の運用ポリシー

1. hippie-expand (M-/)
2. completion-preview (C-i, M-i)
3. dabbrev-expand or cape-dabbrev
4. completion-at-point with corfu and hotfuzz (C-M-i)

corfu-modeはONだが corfu-auto は nil にしておく。

入力中、基本はhippie-expandで候補検索・確定する。completion-previewでお目当ての候補が表示されたら C-i で候補確定してOK。completion-previewによる補完中はM-n, M-p で他の候補を選択できる。M-iを使えばcompletion-previewの枠組みで部分補完しつつ、fallbackしてcorfuのUIで補完開始もできる。

最終手段としてC-M-iを残しておく。