Author Archives: AKIYAMA

2022-08-15

companyからcorfuへ移行~自動と手動で補完候補を変える

corfucompanyよりは幾分素直で扱いやすい印象。

corfuの設定:

(2024-02-18追記: Corfuの自動補完で候補の存在を伝える事と候補を選べるようにする事を分離するで設定を書き直したので以下のコードは古い)

(setq corfu-cycle t) ;; 候補の最初と最後を行き来出来るようにする。
(setq corfu-auto t) ;; 自動的に補完候補を出す。
(setq corfu-preselect 'prompt) ;; 最初の候補を選択しない。誤入力が多すぎるので。

;; 無選択時のRETはquitだけでなく改行もする。
;; (2024-02-15修正:my-corfu-だと素早くC-M-iの後素早くRETを押したときに正しく補完されない。コマンド名がcorfu-で始まっているときだけupdateしている場所があるので)
(defun corfu-my-insert-or-newline ()
  (interactive)
  (if (>= corfu--index 0)
      (corfu--insert 'finished)
    (corfu-quit)
    ;; (2024-02-15修正:インタラクティブじゃないとインデントされなかったりする)
    (call-interactively 'newline)))
(with-eval-after-load "corfu"
  (define-key corfu-map (kbd "RET") 'corfu-my-insert-or-newline))

(global-corfu-mode)

;; lsp-modeでcorfuを使う。
;; (see: https://github.com/minad/corfu/wiki#example-configuration-with-flex)
(setq lsp-completion-provider :none)
(defun my-lsp-mode-setup-completion ()
  (setf (alist-get 'styles
                   (alist-get 'lsp-capf completion-category-defaults))
        '(flex)))
(add-hook 'lsp-completion-mode-hook #'my-lsp-mode-setup-completion)

;; corfuの候補リストにアイコンを表示する。
(setq kind-icon-default-face 'corfu-default)
(add-to-list 'corfu-margin-formatters #'kind-icon-margin-formatter)

(corfu-auto=tで)自動的に補完候補を出しつつ自動的に最初の候補を選択する(corfu-preselectが'firstや'valid)というのは誤操作を引き起こす可能性がある。 何かを入力してRETやTABを押したら自動的に出てきた補完候補の方を意図せず選んでしまったというようなことが起こりえる。そのためにcorfu-preselectを'promptにして明示的に選ばなければ補完しないように設定した。

それでも何かを入力してC-n RETを押すようなシチュエーションでは下に移動して改行するつもりが最初の候補を選択して確定してしまうので、結局誤入力は完全には回避できなかった。なので自動的に出す時の候補をできるだけ少なくする(自動ではあまり候補を出さない)ようにしてみた。補完候補(バックエンド)は completion-at-point-functions (capf)という元々Emacsに備わっている仕組みに一本化されているので、それを調整する。

completion-at-point-functionsの設定:

;; 補完候補を出すときの文脈を特定

(defvar my-capf-context nil)

;; (2022-10-26修正: corfu--auto-complete => corfu--auto-complete-deferred)
(defun my-capf--corfu--auto-complete-deferred (old-fun &rest args)
  ;; corfu-autoの作用で補完候補を出すときに呼び出される。
  (let (;; 自動で補完候補を出す文脈だということを変数に記録する。
        (my-capf-context 'in-corfu--auto-complete-deferred)
        ;; 2024-02-14追記:
        ;; 自動で補完候補を出すときは必ずbasicスタイルのみを使うべし!
        ;; 先頭すら一致していない候補をバリバリ出されたら鬱陶しい!
        (completion-styles '(basic)))
    ;; 元の処理
    (apply old-fun args)))
(advice-add 'corfu--auto-complete-deferred :around #'my-capf--corfu--auto-complete-deferred)

;; 追加の補完関数

(defun my-capf-additional ()
  (pcase my-capf-context
    ('in-corfu--auto-complete-deferred
     ;; 自動補完の場合は確度の高い候補しか出さない。
     nil)
    (_
     ;; 手動補完の場合は積極的にいろんな候補を出す。
     (my-capf-manual))))
(add-hook 'completion-at-point-functions #'my-capf-additional 100)

;; 手動補完時の補完関数

(defvar my-capf-manual nil)
(defun my-capf-manual ()
  ;; capeパッケージの読み込みを遅延させる。
  (unless my-capf-manual
    (setq my-capf-manual
          ;; いろんな補完候補を合成する。
          (cape-super-capf
           #'cape-file #'cape-dabbrev #'cape-abbrev #'cape-line)))
  (funcall my-capf-manual))

自動的に補完候補を出す場合は元々モードに備わっているような補完候補しか出さないようにした。プログラミング言語用のモードの場合は文法に則した候補が出るのでそれほど邪魔にならないと思われる。

逆にC-M-iで手動で補完候補を出す場合はcapeパッケージの多種多様な補完候補を利用する。手動で出しているのだから多少確度の低い候補が出てきても構わない。

cape-super-capfで複数のバックエンドをマージしている。

capeは他にもcompany用バックエンドをcapfに変換するアダプタも持っている。手元にはorg-modeの「#+」行をより良く補完する自作のCompanyバックエンドがあるので、このアダプタでcorfu用に変換した。

今のところM-/はdabbrev-expandのままにしてある。

補完候補については不満なところがまだまだ沢山あるので逐一直していくつもりだ。completion-stylesも過剰な補完生成に一役買っているように見える。verticoにせよcorfuにせよ、どうも補完することばかりを優先して補完させたくないケースを軽視しているように見える。

(2024-02-15追記: Corfuの自動補完で候補の存在を伝える事と候補を選べるようにする事を分離するで設定を追加した)

2022-08-14

2022年夏の新番組

タイトル 開始 時刻 配信
スプリガン -     Netflix
BASTARD!!(バスタード)-暗黒の破壊神- -     Netflix
× 神クズ☆アイドル 07/02 12:00 dアニメ
彼女、お借りします 第2期 -      
iiiあいすくりん2 07/02 08:00 dアニメ
むさしの! 07/02 23:30 dアニメ
リコリス・リコイル 07/02 23:30 ABEMA
シュート!Goal to the Future 07/08 22:00 dアニメ
× てっぺんっ!!!!!!!!!!!!!!! 07/02 22:00 ABEMA
Engage Kiss 07/02 25:00 dアニメ
うたわれるもの 二人の白皇 -      
× 連盟空軍航空魔法音楽隊ルミナスウィッチーズ 07/03 23:00 dアニメ
RWBY 氷雪帝国 07/03 23:00 dアニメ
× 森のくまさん、冬眠中。 07/03 25:00 dアニメ
ユーレイデコ 07/03 23:30 dアニメ
ようこそ実力至上主義の教室へ 2nd Season -      
カードファイト!! ヴァンガード will+Dress (Season3) -      
転生賢者の異世界ライフ ~第二の職業を得て、世界最強になりました~ 07/04 24:00 ABEMA
邪神ちゃんドロップキックX(第3期) -      
オーバーロードⅣ(第4期) -      
× 東京ミュウミュウ にゅ~♡ 07/09 00:00 dアニメ
金装のヴェルメイユ 07/10 00:00 dアニメ
メイドインアビス -烈日の黄金郷-        
新テニスの王子様 U-17 WORLD CUP        
異世界迷宮でハーレムを 07/13 00:30 dアニメ
異世界おじさん 07/06 23:30 dアニメ
× 継母の連れ子が元カノだった -      
× 咲う アルスノトリア すんっ! 07/11 00:00 dアニメ
ちみも 07/09 00:00 dアニメ
組長娘と世話係 07/07 22:30 dアニメ
それでも歩は寄せてくる 07/07 02:28 Amazon
よふかしのうた 07/08 12:00 dアニメ
惑星のさみだれ 07/08 12:00 dアニメ
× プリマドール 07/09 01:00 dアニメ
シャドーハウス 2nd Season -      
× ブッチギレ! 07/09? 22:30 Amazon
メガトン級ムサシ シーズン1特別篇 -      
Extreme Hearts 07/10 02:00 dアニメ
Extreme Hearts SxSxS 07/10 02:00 dアニメ
黒の召喚士 07/09 22:00 dアニメ
Dr.STONE 龍水(TVスペシャル)        
ハナビちゃんは遅れがち 07/10 22:00 dアニメ
異世界薬局 07/10 23:00 dアニメ
× KJファイル 07/11 12:00 dアニメ
オリエント-淡路島激闘編- 07/15 00:00 dアニメ
× シャインポスト        
5億年ボタン~菅原そうたのショートショート~ 07/15 00:30 dアニメ
はたらく魔王さま!!(第2期)        
ラブライブ!スーパースター!! 第2期        
ダンジョンに出会いを求めるのは間違っているだろうかIV        
× 最近雇ったメイドが怪しい 07/26 02:30 dアニメ
BanG Dream! Morfonication        
うたの☆プリンスさまっ♪ マジLOVEスターリッシュツアーズ~旅の始まり~        
風都探偵(仮面ライダーW続編)       U-NEXT
夜は猫といっしょ 08/03   YouTube
賭ケグルイ双(ツイン) 08/04     Netflix
リラックマと遊園地(第2期)       Netflix
D4DJ Doubel Mix        
僕のヒーローアカデミア 【アニメ新作オリジナルエピソード×2話】        
ROLY POLY PEOPLES        
トニカクカワイイ ~制服~(新作エピソード)        
× 世界の終わりに柴犬と(※漫画動画)        
アイドルランドプリパラ        

ちみもの三姉妹の目の描き方が三人とも違うのが好き。

2022-05-04

Emacs Lispでline-prefixを使うと画像上のマウスイベントの座標がずれる件

再現コード:

(progn
  (require 'svg)
  (goto-char (point-min))
  ;; (insert "\n") ;;これがあると発生しづらい(スクロールして画像を一番上にもってくれば発生する)
  (let ((ov (make-overlay (point) (progn (insert " ") (point)) nil t)))
    (overlay-put ov 'evaporate t)
    (overlay-put ov 'pointer 'arrow)
    (overlay-put ov 'line-prefix "XXX") ;;これが無ければ発生しない(実際の状況ではこれが無くてもorg-indentがテキストプロパティのline-prefixを変更するといったことでこれと同じ事になる)
    (overlay-put ov 'display
                 (let ((svg (svg-create 400 300)))
                   (svg-rectangle svg 0 0 400 300 :fill "blue")
                   (svg-text svg "Click here"
                             :x 200 :y 150
                             :text-anchor "middle" :fill "white")
                   (svg-image svg)))
    (overlay-put ov 'keymap
                 (let ((km (make-sparse-keymap)))
                   (define-key km [mouse-1]
                     (lambda (ev)
                       (interactive "e")
                       (message "xy=%s" (posn-object-x-y (event-start ev)))))
                   km)))
  (insert "\n"))

上のコードをscratchバッファで実行すると画像が表示される。その画像をクリックするとミニバッファに座標が表示されるが、画像の左上からの座標になっていないことが分かる。原点は画像の左上では無く「XXX」の左上になっている。

画像の左端、line-prefix文字列の上端付近をクリックしたときの座標
図1: 画像の左端、line-prefix文字列の上端付近をクリックしたときの座標

この現象は画像がウィンドウの先頭にあり、その左にline-prefixによる文字列があるときのみ起こる。(line-prefixはテキストプロパティオーバーレイプロパティline-prefix変数のいずれで設定しても現象は起きる)

Emacs Lisp Reference Manualの22.7.4 Click Eventsには、マウスイベントのdx,dyはobjectの左上角を(0 . 0)とする相対座標と書かれている。今回の場合はobjectは画像('imageで始まるdisplayプロパティ)になるので、画像の左上からの座標になっていなければならない。しかし、画像がウィンドウの先頭にあって、かつ、line-prefixが使われている時に限り、line-prefix文字列の左上からの座標になってしまっている。画像がウィンドウの先頭に無いときや、line-prefixが使われていないときは正しく画像左上からの座標になっている。動作に一貫性が無く、文書化されている仕様と異なる結果が生じているのでEmacsの問題と言って良いのではないかと思う。

回避策としては、マウスの入力を必要とする時だけline-prefixを無効化するといったことが考えられる。

実際にel-easydrawにおいて作図エディタがウィンドウの一番上にあるときに座標がずれる問題が発生していた。org-indentを有効にすると発生する。org-indentはline-prefixやwrap-prefixを使用するので、まさに今回のケースに当てはまる。コミット701bfaaにて一時的にline-prefixを無効化することで回避した。

おそらくEmacsのバグだと思うが、この問題に気がつく人はどのくらいいるのだろうか。Emacs内に画像を表示してその中に対してマウス入力するなんてことを伊達や酔狂で無く本気でやっている人はどのくらいいるのだろうか。