求助在实现自己简单的minibuffer补全需求中遇到的问题

具体思路: 通过自带的abbrev-mode,在abbrev_defs里面设定四个字符作为严格的缩写词,缩写内容为次常用命令和dired复制的常用目录的绝对路径

"mate" "/home/mate/.emacs.d/"
"test" "eval-defun"

表现行为: 输入缩写词之后,自动用expand-abbrev展开,再调用exit-minibuffer完成

代码如下:

(setq abbrev-mode t)
(setq abbrev-file-name "~/.emacs.d/temp/abbrev_defs"

(defun four-chars ()
  (when (and (minibufferp)
             (= (length (minibuffer-contents)) 4))
    (abbrev-mode 1)
    (expand-abbrev)
    (exit-minibuffer)))

(defun custom-execute-extended-command ()
  (interactive)
  (add-hook 'post-command-hook 'four-chars)
  (execute-extended-command nil)
  (remove-hook 'post-command-hook 'four-chars))

(defun custom-dired-do-copy ()
  (interactive)
  (add-hook 'post-command-hook 'four-chars)
  (dired-do-copy nil)
  (remove-hook 'post-command-hook 'four-chars))

存在问题:

  1. 在上面两个custom-函数执行的时候,不管是不是缩略词,都会在四个字符之后自动执行,我只能尽可能把自己用到的命令和路径都写进去,算是一种掩耳盗铃式的解决方式
  2. 当使用那些会自动调用minibuffer的命令时候,包括但不限于replace-string,isearch等等,我的解决方案是使用major-mode-map里面绑定的单键快捷键,如果是通过custom-execute-extended-command来调用,就会继承四个字符自动调用的操作,没法用了,不知道怎么解决
  3. (custom-)dired-do-copy默认执行之后会插入当前路径,但是我缩写词使用的是绝对路径,所以必须把这路径删除
(advice-add 'dired-do-copy :after #'delete-backward-sentence)

这个并没有效果,有人指点我说after之后要加一个定时器,但是我不知道怎么弄,故求助这个,是刚需

我是一个纯菜鸟,这也是一个功能很简单的minibuffer补全,如果有和我类似这样需求的朋友们可以尝试一下,最好emacs -Q来测试

最后就是感谢所有提出建议和解决方案的朋友们,非常感谢大家


(setq abbrev-mode t)
(setq abbrev-file-name "~/.emacs.d/temp/abbrev_defs")
(setq save-abbrevs t)


(defun is-our-abbrev-p (str)
  "检查输入的字符串是否是我们定义的缩写词之一"
  (and (= (length str) 4)  ; 确保长度为4
       (assoc str abbrev-table-name-list)))  ; 检查是否在缩写表中

;; 改进的 four-chars 函数
(defun four-chars ()
  "只在输入的是已定义的缩写词时才展开并退出"
  (when (and (minibufferp)
             (is-our-abbrev-p (minibuffer-contents)))
    (abbrev-mode 1)
    (expand-abbrev)
    (exit-minibuffer)))

;; 创建一个通用的 minibuffer 包装函数
(defun with-abbrev-completion (orig-fun &rest args)
  "为函数添加缩写补全功能的包装器"
  (unwind-protect
      (progn
        (add-hook 'post-command-hook #'four-chars)
        (apply orig-fun args))
    (remove-hook 'post-command-hook #'four-chars)))

;; 包装 execute-extended-command
(defun custom-execute-extended-command ()
  (interactive)
  (with-abbrev-completion #'execute-extended-command nil))

;; 包装 dired-do-copy
(defun custom-dired-do-copy ()
  (interactive)
  (with-abbrev-completion #'dired-do-copy nil))

;; 修复 dired-do-copy 后的路径问题
(defun clear-minibuffer-after-delay ()
  "清除 minibuffer 中的内容"
  (when (minibufferp)
    (delete-region (minibuffer-prompt-end) (point-max))))

(advice-add 'dired-do-copy
            :after
            (lambda (&rest _)
              (run-with-timer 0 nil #'clear-minibuffer-after-delay)))

;; 为特定命令禁用缩写补全
(defvar commands-without-abbrev
  '(replace-string isearch-forward isearch-backward)
  "不需要缩写补全的命令列表")

(advice-add 'execute-extended-command
            :around
            (lambda (orig-fun &rest args)
              (let ((this-command (car (where-is-internal 
                                      (intern (completing-read 
                                             "M-x " obarray 'commandp t))
                                      nil t))))
                (if (memq this-command commands-without-abbrev)
                    (apply orig-fun args)
                  (apply #'with-abbrev-completion orig-fun args)))))
1 个赞

首先非常感谢这位大佬的方案

其次,我刚使用emacs -Q来测试这段代码,只是多了(require 'dired),以便把c和o绑定到两个custom命令上面,同时打开了debug,但是在执行custom-execute-extended-command和custom-dired-do-copy并没有出现预期的结果,还是默认的行为,整个过程也没有报错,不知道为什么

abbrev_defs里面我只写了minibuffer-mode-abbrev-table

是不是我哪里没用对,大佬指点一下