consult-lineのmigemo化に向けた試作

consult-lineはswiperの代替として便利に使える。これをmigemo化したいということである。
プロンプトに続いて検索したい日本語を「ローマ字読み」入力すると、それをもとにconsult-lineが走るという、単純なラッパーである。

(defun consult-line-migemo ()
  (interactive)
  (let ((input (read-string "Input: " nil)))
    (consult-line (migemo-get-pattern input))))

isearch中に発動したいならこちら。以下の設定ではisearch中に"C-:"とすると、そのクエリをmigemo化してconsult-lineが発動する。

(define-key isearch-mode-map (kbd "C-:") 'consult-line-migemo-isearch)
(defun consult-line-migemo-isearch ()
  (interactive)
  (consult-line (migemo-get-pattern isearch-string)))


通常の"C-s"でisearch-forward, "C-u C-s"でカーソル下のシンボルを初期値にしたconsult-line、"C-u C-u C-s"でconsult-line-migemoが発動する関数も書くことができる(selectrum-modeの使用を前提)。

(defun consult-line-symbol-at-point ()
  (interactive)
  (consult-line (thing-at-point 'symbol)))

(defun my-isearch-or-consult (use-consult)
  (interactive "p")
  (cond ((eq use-consult 1)
         (call-interactively 'isearch-forward))
        ((eq use-consult 4)
         (call-interactively 'consult-line-symbol-at-point))
        ((eq use-consult 16)
         (call-interactively 'consult-line-migemo))))

(global-set-key (kbd "C-s") 'my-isearch-or-consult)

もし C-u C-sでconsult-lineにデフォルト入力されたシンボルを消したい場合は、

(define-key selectrum-minibuffer-map (kbd "C-l") #'selectrum-backward-kill-sexp)

とすると"C-l"で消せる。

selectrum/consult/marginaliaの設定

ivy/counsel系の代替として、selectrumやconsultが最近来ている。

特徴をまとめた方がいる:

  • selectrum … ivyに対応。ivyよりも実装はシンプル。
  • consult ... counselに対応。counselにはないユニーク機能を持つコマンドを提供したり。consult-bufferはその例。
  • marginalia ... ivy-richに対応。

最近使っているので設定を残す。
まずはseletrumとmarginalia-mode。

(eval-when-compile (require 'selectrum))
(eval-when-compile (require 'selectrum-prescient))
(declare-function selectrum-mode "selectrum")
(declare-function selectrum-prescient-mode "selectrum-prescient")

(require 'selectrum)
(selectrum-mode +1)
(define-key selectrum-minibuffer-map (kbd "C-l") #'selectrum-backward-kill-sexp)
(setq selectrum-max-window-height 20)

;; to make sorting and filtering more intelligent
(require 'selectrum-prescient)
(selectrum-prescient-mode +1)
(require 'prescient)
(prescient-persist-mode 1)
(setq prescient-aggressive-file-save t)
;; selectrum-prescient-enable-filtering : t
;; selectrum-prescient-enable-sorting : t

;; selectrumのivy-rich
;; https://github.com/minad/marginalia
(require 'marginalia)
(marginalia-mode +1)
(setq marginalia-align-offset 25) ; 右端にどれだけスペースを詰めるか
;; (setq marginalia-truncate-width 80)
;; (setq marginalia-margin-threshold 200)
;; (setq marginalia-separator-threshold 120)

;; marginalia-annotators 初期値およびサイクル順を指定する
(setq marginalia-annotators
      '(marginalia-annotators-light marginalia-annotators-heavy nil))

;; marginalia-annotatorsをサイクルする
(define-key minibuffer-local-map (kbd "C-M-a") #'marginalia-cycle)

;; When using Selectrum, ensure that Selectrum is refreshed
;; when cycling annotations.
(advice-add #'marginalia-cycle :after
            (lambda () (when (bound-and-true-p selectrum-mode)
                         (selectrum-exhibit 'keep-selected))))

次はconsult。

追記
consultについては設定を見直した。
tam5917.hatenablog.com

以下の設定は古いものになる。

(require 'consult)

;; consult-line ... swiper の代替
;; consult-isearch ... isearch中にconsultインタフェースでクエリを再入力し、isearch再実行
;; consult-multi-occur ... multi-occurの代替
;; consult-focus-line ... クエリにヒットする部分のみを抽出して「表示」する(他が隠れる, narrowing)。
;; その後、C-uつきで呼び出すと、隠れていた部分が表示される(もとに戻る, widen)
;; consult-recent-file ... 最近開いたファイルを選択

(setq consult-async-refresh-delay 0.2)

(global-set-key (kbd "C-z C-n") 'consult-recent-file)

;; consult-buffer時などにおけるpreview機能の調整
;; (setq consult-preview-key nil) ; 常にプレビューOFF
;; (setq consult-preview-key 'any) ; 常にプレビューON
;; (setq consult-preview-key (kbd "C-M-p")) ; これを押したときだけプレビューON

;; 以下のコマンド群については、M-.でプレビューする。それ以外はON
(consult-customize
 consult-ripgrep consult-git-grep consult-grep
 consult-bookmark consult-recent-file consult-xref
 consult--source-file consult--source-project-file consult--source-bookmark
 :preview-key (kbd "M-."))

;; カーソル下のシンボルを拾ってconsult-line発動
(defun consult-line-symbol-at-point (&optional at-point)
  (interactive)
  (if at-point
      (consult-line (thing-at-point 'symbol))
    (consult-line)))

(defun my-isearch-or-consult (use-consult)
  (interactive "p")
  (cond ((eq use-consult 1)
         (call-interactively 'isearch-forward))
        ((eq use-consult 4)
         (call-interactively 'consult-line-symbol-at-point))
        ((eq use-consult 16)
         (call-interactively 'consult-line))))

(global-set-key (kbd "C-s") #'my-isearch-or-consult)
(define-key isearch-mode-map (kbd "C-:") #'consult-isearch) ; 実質的な絞り込み
(define-key isearch-mode-map (kbd "C-;") #'consult-line) ; swiperに対応

;; 絞り込み対象をバッファに限定 : b SPC
;; 絞り込み対象をファイル(recentf)に限定 : f SPC
;; 絞り込み対象をブックマークに限定 : m SPC
(global-set-key (kbd "C-x b") #'consult-buffer)

;; consult--source-fileの公式実装では「現在バッファとして保持しているファイル」が
;; ファイル(recentf)の検索対象から除外されるので、
;; それをやめるため単純にitemsにrecentf-listだけを持ってくる。
;; この設定の影響範囲はconsult-bufferのみ。consult-recent-fileは影響を受けない。
(setq consult--source-recent-file
  `(:name     "File"
    :narrow   ?f
    :category file
    :face     consult-file
    :history  file-name-history
    :action   ,#'consult--file-action
    :enabled   ,(lambda () recentf-mode)
    :items ,recentf-list))

;; consult-grepがデフォルトで複数ファイルを対象にした串刺し検索なので、
;; 検索対象をカレントバッファ(単一ファイル)に限定
(defun consult-grep-one-file ()
  "Call `consult-grep' for the current buffer (a single file)."
  (interactive)
  (let ((consult-grep-args
         (concat "grep "
                 "--line-buffered "
                 "--color=never "
                 "--line-number "
                 "--with-filename "
                 (shell-quote-argument buffer-file-name))))
    (consult-grep)))

;; consult-ripgrepがデフォルトで複数ファイルを対象にした串刺し検索なので、
;; 検索対象をカレントバッファ(単一ファイル)に限定
(defun consult-ripgrep-one-file ()
  "Call `consult-ripgrep' for the current buffer (a single file)."
  (interactive)
  (let ((consult-ripgrep-args
         (concat "rg "
                 "--line-buffered "
                 "--color=never "
                 "--line-number "
                 "--no-heading "
                 "--max-columns=250 "
                 "--smart-case "
                 "--max-columns-preview "
                 "--with-filename "
                 (shell-quote-argument buffer-file-name))))
    (consult-ripgrep)))

(global-set-key (kbd "M-g e") 'consult-compile-error)
(global-set-key (kbd "M-g f") 'consult-flymake)
(global-set-key (kbd "M-g g") 'consult-goto-line)
(global-set-key (kbd "M-g M-g") 'consult-goto-line)
(global-set-key (kbd "M-g o") 'consult-outline)
(global-set-key (kbd "M-g m") 'consult-mark)
(global-set-key (kbd "M-g k") 'consult-global-mark)
(global-set-key (kbd "M-g i") 'consult-imenu)
(global-set-key (kbd "M-g I") 'consult-imenu-multi)
(global-set-key (kbd "M-y") 'consult-yank-pop)
(global-set-key (kbd "M-s f") 'consult-find)
(global-set-key (kbd "M-s F") 'consult-locate)
(global-set-key (kbd "M-s g") 'consult-grep)
(global-set-key (kbd "M-s G") 'consult-git-grep)
(global-set-key (kbd "M-s r") 'consult-ripgrep)
(global-set-key (kbd "M-s l") 'consult-line)
(global-set-key (kbd "M-s L") 'consult-line-multi)
(global-set-key (kbd "M-s m") 'consult-multi-occur)
(global-set-key (kbd "M-s k") 'consult-keep-lines)
(global-set-key (kbd "M-s u") 'consult-focus-lines)

;; linum表示中の行番号表示は邪魔なので消す
(setq consult-goto-line-numbers nil)

(declare-function persp-get-buffer-names "perspective")
;; Use consult-buffer with perspective-el.
;; This would hide the default consult--source-buffer,
;; and show the list of perspective buffers on the top
(with-eval-after-load "perspective"
  (consult-customize consult--source-buffer :hidden t :default nil)
  (defvar consult--source-perspective
    (list :name     "Perspective"
          :narrow   ?s
          :category 'buffer
          :state    #'consult--buffer-state
          :default  t
          :items    #'persp-get-buffer-names))

  (push consult--source-perspective consult-buffer-sources))

AdaCosのPyTorch実装にまつわるバグ

深層距離学習の一つであるAdaCosはとても有効である。

PyTorch実装も利用できる。 github.com

ところがこの実装には(2021/04/24時点)、使い方を間違えるとNaNが頻発する不具合がある。 forward関数内でscaleをadaptiveに更新しているのだが、学習データのみ更新の対象としてほしいところ、検証データの上でそのまま動かすと(forward関数を必然的に通すため)スケールが更新され続けてしまう。結果としてscaleが発散する現象が起きる。 この問題点は以下のissueで指摘されており、修正案も示されている。

github.com

この修正案を適用することで、NaNの発生は抑制される。

ivy/swiperをmigemo化した際に遭遇したエラーの解決と新たなminor-modeの提案

はじめに

avy-migemoを使わずにswiperやswiper-isearchをmigemo化することに成功した偉大な先人がいる:

これらの記事では、ivyに食わせるmigemo的な正規表現を生成する関数を独自に定義している。
そして、これらの設定を適用したのち、swiper-isearchを実行したところ、以下のエラーに遭遇した。

Error in post-command-hook (ivy--queue-exhibit): (invalid-regexp "Unmatched ) or \\)")

この原因は以下の設定をしていたことにあった:

 (setq search-default-mode #'char-fold-to-regexp)

そこで以下のように修正することで、エラーは解消され、件のmigemo化が実現できた。

 (setq search-default-mode nil)

毎回、search-default-modeを評価しなおすのも手間なので、オリジナルのminor-modeを作ることにした。
このminor-modeの紹介が本記事の主旨である。

作成したminor-modeおよび使い方

以下のivy-with-migemo-modeである。

コードを表示する

本minor-modeを有効化することで、swiperやswiper-isearchでmigemoが使えるようになる。すなわち、有効化されている間はsearch-default-modenilに設定される。

(require 'ivy-with-migemo)
(setq ivy-with-migemo-enable-command
      '(swiper swiper-isearch))
(global-ivy-with-migemo-mode 1)

上記、ivy-with-migemo-enable-commandという変数が、migemoを使う対象となるコマンドを格納するリストである。counsel-recentfを対象に追加する場合は、以下の設定にする。

(setq ivy-with-migemo-enable-command
      '(swiper
        swiper-isearch
        counsel-recentf))

無効化する場合は、C-u -1 M-x global-ivy-with-migemo-modeとすればよい。もしくは

(global-ivy-with-migemo-mode -1)

を評価する。

バッファローカルで有効・無効を切り替える場合は、global-ivy-with-migemo-modeによる有効化を使わず各バッファごとにM-x ivy-with-migemo-modeとする

もしcounsel-agやcounsel-rgも併せて使う場合は以下の設定を試してみるとよい。ただしripgrepは最新版のソースコードをダウンロードしてコンパイル/インストールしておくこと。複数ファイルの串刺し検索がmigemoで実現できるので、とても快適である。

(require 'ivy-with-migemo)
(setq ivy-with-migemo-enable-command
      '(swiper swiper-isearch counsel-rg counsel-ag))
(setq migemo-options '("--quiet" "--nonewline" "--emacs"))
(migemo-kill)
(migemo-init)
(global-ivy-with-migemo-mode 1)

補足

ivyインターフェイスを持つコマンドに対してmigemoを有効化するためのパッケージとしては、すでにivy-migemoが存在する。

このパッケージも同様の機能を提供するが、search-default-modeの設定には踏み込んでいない。さらにmigemo化のON/OFFには専用のtoggleコマンドを用意しており、minor-modeは用意されていない点も異なる。もっとも、toggleコマンド自体は良いインターフェイスであると思う。またivy-migemoではfuzzyマッチングも利用でき、ユーザによっては嬉しい点である。以上がivy-migemoivy-with-migemo-modeとの違いである。

追記 4/23

最近の変更により、ivy-migemoにもsearch-default-modeへの対応が入ったようである。かなり使いやすく整備されており、おすすめできる。

追記

ivy-with-migemo-modeをきれいに整形し、githubリポジトリを登録した。
名前はswiper-migemoに改めた。

LaTeXソースを保存と同時にコンパイルしPDFを作成するための設定(smart-compile.elとlatexmk利用)

以下の記事にて、表題に関する設定が公開されていた。「自動コンパイル」のところである。

銭谷誠司氏のサイトにて公開されているsmart-compile.elを用いている。上記の記事ではリンク切れであったが、現在はgithubにて継続的に開発が続いている。

smart-compile.el自体のコンセプトは明解で, さまざまなソースファイルと対応するコンパイルコマンドをひもづけるためのインターフェイスを提供する。これをLaTeXソースのコンパイルに応用したのが上記の設定である。

現在はオリジナルから多少変更を加えて以下の設定を利用している(AUCTeX利用)。latexmkをバックグラウンドで走らせている。

;; 保存と同時にlatexmkを走らせる
(eval-when-compile (require 'smart-compile))
(declare-function smart-compile-string "smart-compile")
(defun run-latexmk ()
  (when (string-match ".tex$" (buffer-file-name))
    (let ((buf (get-buffer "*Background TeX proccess*")))
      (if (bufferp buf) (kill-buffer buf)) ) ;; flush previous log
    (require 'smart-compile) ;; for smart-compile-string
    (start-process-shell-command
     "Background TeX" "*Background TeX proccess*"
     (smart-compile-string "latexmk %f"))))
(define-minor-mode AutoTeX-mode
  "Mode for compiling latex sources and creating PDFs after saving."
  :global nil
  :lighter " Auto"
  (if AutoTeX-mode
      (add-hook 'after-save-hook 'run-latexmk t t)
    (remove-hook 'after-save-hook 'run-latexmk t)))

(add-hook 'TeX-mode-hook #'(lambda () (AutoTeX-mode 1)))

after-save-hookに引っ掛けることで、保存と同時にlatexmkを走らせてLaTeXソースをコンパイルしPDFを生成する。smart-compile-stringにより実行される、latexmkへのオプション引数にはカスタマイズの余地はある。

ちなみにlatexmkの設定ファイル(.latexmkrc)の設定例は以下の通りである。

#!/usr/bin/env perl

$pdf_mode = 3;

$latex  = "uplatex -synctex=1 -halt-on-error -interaction=nonstopmode %O %S";
$dvipdf = 'dvipdfmx %O -o %D %S';
$bibtex = 'upbibtex %O %S';
$biber = 'biber --bblencoding=utf8 -u -U --output_safechars %O %S';
$makeindex = 'mendex %O -o %D %S';
$max_repeat = 5;

個人的にpdf-toolsを用いているため、外部アプリによるプレビュー関連の設定はしていない。もしプレビューワの設定をするならば、$pdf_previewerという変数に、プレビューワ起動に必要なコマンドラインを設定する。例えば

$pdf_previewer = 'open -a /Applications/Preview.app';

$pdf_previewer = 'evince %O %S';

などである。そのうえで上記のrun-latexmk関数において以下の修正を施し、latexmkに-pvオプションをつけることで、プレビューワを起動する。

(smart-compile-string "latexmk -pv %f")

マウスホイールによる連続スクロールのスピードを調節 on Emacs

マウスホイールにより連続スクロールすると画面がどんどん飛んでしまうので、それを抑制する設定。

(global-set-key [mouse-5] '(lambda () "" (interactive) (scroll-up 1)))
(global-set-key [mouse-4] '(lambda () "" (interactive) (scroll-down 1)))
(global-set-key [double-mouse-5] '(lambda () "" (interactive) (scroll-up 1)))
(global-set-key [double-mouse-4] '(lambda () "" (interactive) (scroll-down 1)))
(global-set-key [triple-mouse-5] '(lambda () "" (interactive) (scroll-up 2)))
(global-set-key [triple-mouse-4] '(lambda () "" (interactive) (scroll-down 2)))
(global-set-key [wheel-down] '(lambda () "" (interactive) (scroll-up 1)))
(global-set-key [wheel-up] '(lambda () "" (interactive) (scroll-down 1)))
(global-set-key [double-wheel-down] '(lambda () "" (interactive) (scroll-up 1)))
(global-set-key [double-wheel-up] '(lambda () "" (interactive) (scroll-down 1)))
(global-set-key [triple-wheel-down] '(lambda () "" (interactive) (scroll-up 2)))
(global-set-key [triple-wheel-up] '(lambda () "" (interactive) (scroll-down 2)))

summarye.elの設定 改訂版

かつてsummarye.elの設定に関する記事を書いたことがあるが、今回いくつか見直した。

display-line-numbers-modeによる行番号の表示を止めた。これをしないと行番号がsummarye由来ものと合わせて2重に行番号が表示されるからである。またサマリがポップアップする位置とサイズをshackleにより制御した。つねにframeの半分以上を占拠してポップアップするsummaryeデフォルトの挙動が不満だったからである。

(eval-when-compile (require 'summarye)
                   (require 'view)
                   (require 'shackle))
(declare-function View-quit "view")

;; fで当該バッファのサマリ位置にジャンプ
;; SPCでカーソル位置のサマリをプレビュー
;; nでプレビューしながら次のサマリを表示
;; bでプレビューしながら前のサマリを表示
;; dでサマリ一覧から削除
;; gでリロード、削除したサマリも復活
;; mでマーク

(autoload 'se/make-summary-buffer "summarye" nil t)
(autoload 'socuur "summarye" nil t)

(define-key help-map "M" 'se/make-summary-buffer)

(defun se/quit-summary-item ()
  "Quit summary."
  (interactive)
  (view-mode 1)
  (View-quit))

(defvar summary-edit-summary-mode-hook nil)
(add-hook 'summary-edit-summary-mode-hook
          #'(lambda ()
              ;; 行番号の表示を止める
              (display-line-numbers-mode -1)

              (define-key summary-edit-mode-map (kbd "q") 'se/quit-summary-item)))

(with-eval-after-load "shackle"
  (add-to-list 'shackle-rules
               '("^function-in-.*" :regexp t :align right :ratio 0.3))
  (add-to-list 'shackle-rules
               '("^Items-in-.*" :regexp t :align right :ratio 0.3)))

バイトコンパイル時の警告を抑制した修正版summarye.elのコードはこちらから。