去掉 helm/ivy/selectrum 使用原生的 completions buffer

多谢PR!这样可以删除了。

问下为何要去ivy化呢,有什么特殊原因?而且也没有选用vertico和selectrum。来几张最后的效果图更好 :smile:

:rofl: 主要是我想仅只用少量一点的包,并且越来越觉得 counsel/swiper 不是那么重要,于是就先把这2个给干掉了。然后就觉得 ivy-mode 是不是也不是那么重要?就尝试了一下去 ivy 化。

总体来说还是想令自己的 Emacs 依赖的外部包少一点

1 个赞

有时候想去掉一个包就是单纯看不顺眼了 :rofl: 我干过好多这样的事情

28内置的project也够用了,其实

理解了。我也干过同样的事情,比如用内置的electric-pair替代smartparens。

去掉一个包容易,但是要去掉一个生态有时挺麻烦的,比如楼上说的project。内置的功能够用了,但是还需要时间发展下生态,第三方扩展包要跟上才行。现在projectile和flycheck我暂时没法去掉,包括ivy,很多好用的功能还没找到替代者 :joy:

BTW,内置的不一定是最好的,当然是最方便的。

1 个赞

https://elpa.gnu.org/packages/aggressive-completion.html

最近才发现的包,配合emacs29原生补全效果很棒,比mct丝滑

1 个赞

这个包是干啥的?minibuffer补全吗?

和mct和楼主的方案类似,使用原生补全时自动化了completion buffer的行为,但更优雅、高效 (个人认为

1 个赞

貌似和vertico功能差不多

我现在也转到 Emacs 29 + aggressive-completion 了。

看起来还是有很多人在推动原生 completions buffer 的。几乎所有的补全框架都是着眼于为 completing-read 函数提供更友好的界面,但是原生的实现实际上才是对输入体验干扰最小的。

依次尝试过下面几种,都是专门针对特定版本的 Emacs 的,作者都不打算继续兼容新版本 Emacs 了,估计内置的 completions buffer 就足够好用的那一天也不远了。

Emacs 27: live-completions

Emacs 28: mct

Emacs 29: aggressive-completion

我刚刚安装了aggressive-completion,只是打开了这个minor mode,比起vertico体验差很多。能分享一下配置吗? @solatle @tangxinfa

;; ignore cases when complete
(setq read-file-name-completion-ignore-case t)
(setq read-buffer-completion-ignore-case t)
(setq completion-ignore-case t)

;; disable help message in the minibuffer
(setq completion-show-help nil)
(setq completion-show-inline-help nil)
(setq completion-auto-help 'always)
(setq completion-styles '(basic partial-completion substring))
(setq completion-category-overrides '((buffer (styles . (substring flex)))
				      (file (styles . (substring flex)))))
(setq completions-format 'one-column)
(setq completions-max-height 20)
(setq completions-detailed t)
(add-to-list 'display-buffer-alist
	     '("\\*Completions\\*"
	       (display-buffer-reuse-window display-buffer-in-side-window)
	       (side . bottom)
	       (slot . 0)))

;; delete a whole repo name in a path
(defun my/minibuffer-backward-kill (arg)
  "When minibuffer is completing a file name delete up to parent
folder, otherwise delete a word"
  (interactive "p")
  (if minibuffer-completing-file-name
      ;; Borrowed from https://github.com/raxod502/selectrum/issues/498#issuecomment-803283608
      (if (string-match-p "/." (minibuffer-contents))
          (zap-up-to-char (- arg) ?/)
        (delete-minibuffer-contents))
    (delete-word (- arg))))
(define-key minibuffer-mode-map (kbd "M-d") #'my/minibuffer-backward-kill)
(define-key minibuffer-local-completion-map (kbd "SPC") nil)
(define-key minibuffer-mode-map (kbd "C-n") #'minibuffer-next-completion)
(define-key minibuffer-mode-map (kbd "C-p") #'minibuffer-previous-completion)

(aggressive-completion-mode 1)

多谢,我试试看。

aggressive-completion 表现迟钝&怪异?

我用最小配置体验了一下:

$ emacsq.sh -P aggressive-completion -M aggressive-completion-mode

也许 aggressive 的意思就是它会自做主张帮用户补齐输入内容, 以缩小候选范围吧,例如当用户输入:

`M-x agg|`

等待 aggressive-completion-delay (默认0.3秒) 时长之后,自动补齐为:(候选项缩小到 3 条)

`M-x aggressive-completion-|`

初次使用有点不知所措,总是“抢拍”。

似乎作者的理念是,只要手速够快,aggressive-completion 就不来“打扰”你就感觉不到 aggressive-completion 的存在。

aggressive-completion-delay 除了控制自动补完输入之外,也控制着弹窗延时。对于 helm/ivy/selctrum 用户来说,总是会在输入之后习惯性的期待弹窗出现,而这个延时就让这群用户觉得有点迟钝。

但又不能把延时设为 0,这样你永远都抢不过 aggressive-completion

另外再试了一下 @solatle #38 楼的配置:

$ emacsq.sh -P aggressive-completion --eval "(progn {code-from-#38})"

由于先前的体验,在我输入 M-x agg 之后,已经不惊讶出现以下画面了:

image

但是当我按下 C-n 的时候,它居然直接选择了第一项,把其它选项都过滤掉了:

image

这完全超出了我的理解范畴。还是我哪里配置/操作不对?

我直接试用了solatle的配置(卸载了vertico),也感觉差不多,补全非常的“aggressive”,操作必须得非常快,稍一停顿,就会把第一个选项作为过滤条件了。而且选好了以后,需要多一次确认。

如果直接从ivy/vertico的逻辑转到aggressive-completion来看确实如楼上二位所说。C-n和C-p是我没用ac之前的配置(纯原生手动toggle completion buffer)保留下来的,用了ac就不经常用了,直接用输入过滤。如果不想那么aggressive可以M-t关闭,比如新建文件的时候,但是C-n C-p还是会像二位所说过滤其它选项,这个确实可以提交改进。

原生的补全机制还在慢慢完善,目前从效果来看肯定没用其它的第三方成熟。但选择原生的人就是喜欢少一点的弹出干扰和简单一致的minibuffer体验,跟ivy/vertico的出发点还是不太一样的,如果没有这方面的偏好,vertico功能性是更完善的,突然变到ac的逻辑肯定不习惯

需设置 aggressive-completion-auto-completenil,我只使用 M-<数字> 来选择候选项.

(defun my/completion-goto-line (n)
  "Select candidate by M-<number> or input a line number.
Just insert selection with prefix argument."
  (interactive
   (list (let* ((type (event-basic-type last-command-event))
                (char (if (characterp type)
                          ;; Number on the main row.
                          type
                        ;; Keypad number, if bound directly.
                        (car (last (string-to-list (symbol-name type))))))
                (n (- char ?0)))
           (if (zerop n) 10 n))))
  (switch-to-completions)
  (let ((line (if (<= n 10) n
                (read-number "Select line: ")))
        (completion-no-auto-exit (equal current-prefix-arg '(4))))
    (goto-line (- line display-line-numbers-offset))
    (choose-completion)))

(defun my/completion-goto-line-define-keys (maps)
  "Setup M-<number> key to select Nth candidate."
  (dolist (map maps)
    (dotimes (i 10)
      (define-key map (read-kbd-macro (format "M-%d" i)) 'my/completion-goto-line))))

  ;; M-<number> to select Nth candidate.
  (my/completion-goto-line-define-keys (list minibuffer-local-completion-map
                                             completion-in-region-mode-map
                                             completion-list-mode-map))

(defun my/completion-list-mode-hook-function ()
    (setq-local mode-line-format nil
                track-eol nil)
    (display-line-numbers-mode +1)
    (hl-line-mode -1)
    (rainbow-mode +1)
    (face-remap-add-relative 'line-number :background 'unspecified :foregound 'unspecified :inherit 'default)
    (face-remap-add-relative 'line-number-current-line :background 'unspecified :foregound 'unspecified :inherit 'default))
  (add-hook 'completion-list-mode-hook #'my/completion-list-mode-hook-function)

Completions buffer + aggressive-completion 补全非常丝滑。 分享下我的配置:

(use-package minibuffer
  :init
  (keymap-unset minibuffer-local-completion-map "SPC")
  :bind
  (:map completion-list-mode-map
	    ("z" . switch-to-minibuffer))
  :custom
  (isearch-allow-scroll t)
  ;(enable-recursive-minibuffers t)
  (minibuffer-depth-indicate-mode t)
  (minibuffer-electric-default-mode t)
  ;; Don't insert completion at point into minibuffer
  (minibuffer-completion-auto-choose nil)
  ;; One frame one minibuffer.
  (minibuffer-follows-selected-frame nil)
  (read-buffer-completion-ignore-case t)
  (read-file-name-completion-ignore-case t)
  (minibuffer-default-prompt-format " [%s]")
  (minibuffer-prompt-properties
   '(read-only t cursor-intangible t face minibuffer-prompt))
  (resize-mini-windows t)
  (completion-auto-help t)
  (completion-show-help nil)
  ;(completion-show-inline-help nil)
  (completion-cycle-threshold nil)
  ;; `t' `second-tab' `nil'
  (completion-auto-select 'seond-tab)
  (completions-detailed t)
  ;; Ignore cases when complete
  (completion-ignore-case t)
  ;; vertical display
  (completions-format 'one-column)
  (completions-max-height 7)
  (completions-sort #'completion:list-sort)
  :config
  ;; Hide the mode line of the Completions buffers
  (add-to-list 'display-buffer-alist
               '("\\`\\*Completions\\*"
                 nil
                 (window-parameters (mode-line-format . none))))

  (defun completion:list-sort (all)
    "对 `Completions-buffer' 中的补全项进行排序"
    (let ((hist (minibuffer-history-value)))
      (thread-first all
                    (sort (lambda (c1 c2) (< (length c1) (length c2))))
                    (sort (lambda (c1 c2) (> (length (member c1 hist))
                                        (length (member c2 hist)))))))))

(use-package all-the-icons-completion
  :straight t
  :hook (after-init-hook . all-the-icons-completion-mode))

(use-package aggressive-completion
  :straight t
  :bind
  (:map aggressive-completion-minibuffer-map
         ("TAB" . completion:auto-select))
  :hook
  (after-init-hook . aggressive-completion-mode)
  :config
  (add-hook 'aggressive-completion-mode-hook #'completion:disable-auto-select)
  (defun completion:disable-auto-select ()
    ;; 避免自动切换至 `Completions-buffer' 中的补全项
    (setq completion-auto-select nil
    ;; 禁止 `minibuffer' 中的补全     
          completion-cycle-threshold nil))
  
  (defun completion:auto-select ()
    "`TAB' 键可切换至 `Completions-buffer' 中的补全项"
    (interactive)
    (let ((completion-auto-select t))
      (minibuffer-complete))))

(use-package orderless
  :straight t
  :custom
  (completion-styles '(orderless basic))
  (completion-category-defaults nil)
  (completion-category-overrides '((file (styles basic partial-completion)))))
1 个赞

能有个视频或者动图展示下有多丝滑就更好了

原生补全保姆级教程来了

4 个赞