目前每头绪, 因为我不用 hydra , 欢迎PR
回车的时候报下面的问题
Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
delete-region(nil 944)
(if (fboundp candidate-expand) (funcall candidate-expand candidate-info bound-start) (delete-region bound-start (point)) (insert (plist-get candidate-info :label)))
(let* ((candidate-info (acm-menu-current-candidate)) (bound-start acm-menu-frame-popup-point) (backend (plist-get candidate-info :backend)) (candidate-expand (intern-soft (format "acm-backend-%s-candidate-expand" backend)))) (if (fboundp candidate-expand) (funcall candidate-expand candidate-info bound-start) (delete-region bound-start (point)) (insert (plist-get candidate-info :label))))
acm-complete()
funcall-interactively(acm-complete)
call-interactively(acm-complete nil nil)
command-execute(acm-complete)
什么文件? 什么模式? 什么情况下复现的?
有没有和 emacs -Q 对比测试过?
请反馈问题详细点, 没有任何环境说明, 谁也不知道啥原因。
因为 lsp-bridge 的异步设计, 每敲一个字符, 都会让LSP Server重新计算补全候选词, 所以我们没有办法在输入字符的时候对LSP候选词进行正则过滤, 因为你新敲的字符就会触发LSP Server用新的候选词覆盖补全菜单的内容。
但是很多 Corfu 的用户经常反馈需要针对LSP候选词进行快速过滤以少敲一点字符提高补全的效率。
今天想了想, 其实有一个妥协的办法, 按一个按键进入过滤模式, 过滤模式开启时, 用户输入的字符不修改 buffer 的内容(这样也就不会触发LSP Server计算新的候选词), 而是通过 after-string overlay 显示到 buffer 上, 这样 acm 再根据 overlay 的内容二次过滤候选词即可达到目标。
使用方法:
- 正常输入, 触发acm菜单弹出
- acm菜单显示后, 按 Alt + u 进入过滤模式
- 接着输入过滤字符串, acm菜单消失时自动退出过滤模式
效果展示:
配置文件内容
(add-to-list 'load-path "~/.emacs.d/site-lisp/lsp-bridge")
(add-to-list 'load-path "~/.emacs.d/elpa/posframe-20221118.614")
(add-to-list 'load-path "~/.emacs.d/elpa/markdown-mode-20221210.348")
(add-to-list 'load-path "~/.emacs.d/elpa/yasnippet-20200604.246")
(require 'lsp-bridge)
(require 'acm)
(add-hook 'text-mode-hook 'lsp-bridge-mode)
(add-hook 'text-mode-hook 'acm-mode)
org 文件内容
* TODO Test
SCHEDULED: <2022-12-18 Sun +1d>
:PROPERTIES:
:LAST_REPEAT: [2022-12-17 Sat 19:01]
:END:
- State "DONE" from "TODO" [2022-12-17 Sat 19:01]
在 heading 上按 C-c C-t,会搞乱 properties的内容。显示如下:
你 C-c C-t 弄乱 org-mode 和 lsp-bridge 有啥关系呢?
C-c C-t
是完成一次任务。
emacs -q 使用 C-c C-t 是正常的。
(add-to-list 'load-path "~/.emacs.d/site-lisp/lsp-bridge")
(add-to-list 'load-path "~/.emacs.d/elpa/posframe-20221118.614")
(add-to-list 'load-path "~/.emacs.d/elpa/markdown-mode-20221210.348")
(add-to-list 'load-path "~/.emacs.d/elpa/yasnippet-20200604.246")
(require 'lsp-bridge)
(require 'acm)
(add-hook 'text-mode-hook 'lsp-bridge-mode)
(add-hook 'text-mode-hook 'acm-mode)
这个最小配置是仅启用了 lsp-bridge,与lsp-bridge无关的话,是和lsp-bridge的依赖有关?
你先自己调试吧, 我不知道。
不知道lsp-bridge为什么会搞乱这个,暂时没有头绪。
你先自己调试吧, 我很少用 org-mode , 没有时间折腾 org-mode
(setq lsp-bridge--internal-hooks
'(
(before-change-functions lsp-bridge-monitor-before-change nil t)
;;(after-change-functions lsp-bridge-monitor-after-change nil t)
(pre-command-hook lsp-bridge-monitor-pre-command nil t)
(post-command-hook lsp-bridge-monitor-post-command nil t)
(after-save-hook lsp-bridge-monitor-after-save nil t)
(kill-buffer-hook lsp-bridge-close-buffer-file nil t)
(find-file-hook lsp-bridge-search-words-update nil t)
(before-revert-hook lsp-bridge-close-buffer-file nil t)
(post-self-insert-hook lsp-bridge-monitor-post-self-insert 90 t)))
定位到了这里,是lsp-bridge-monitor-after-change
这个导致了问题。
你继续注释 lsp-bridge-monitor-after-change 的代码排查吧, 看看具体是哪一行导致的?
emacs -q 在 scratch 中加载:
(add-to-list 'load-path "~/.emacs.d/site-lisp/lsp-bridge")
(add-to-list 'load-path "~/.emacs.d/elpa/posframe-20221118.614")
(add-to-list 'load-path "~/.emacs.d/elpa/markdown-mode-20221210.348")
(add-to-list 'load-path "~/.emacs.d/elpa/yasnippet-20200604.246")
(require 'lsp-bridge)
(require 'acm)
先M-x eval buffer
然后启用M-x lsp-bridge-mode
和 M-x acm-mode
。
在 scratch中回车报下面的错误:
Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
delete-region(nil 445)
(if (fboundp candidate-expand) (funcall candidate-expand candidate-info bound-start) (delete-region bound-start (point)) (insert (plist-get candidate-info :label)))
(let* ((candidate-info (acm-menu-current-candidate)) (bound-start acm-menu-frame-popup-point) (backend (plist-get candidate-info :backend)) (candidate-expand (intern-soft (format "acm-backend-%s-candidate-expand" backend)))) (if (fboundp candidate-expand) (funcall candidate-expand candidate-info bound-start) (delete-region bound-start (point)) (insert (plist-get candidate-info :label))))
acm-complete()
funcall-interactively(acm-complete)
call-interactively(acm-complete nil nil)
command-execute(acm-complete)
不启用acm-mode 没有上面的问题。
系统 MacOS 13, emacs-version 28.2.
你要看看 acm-get-input-prefix-bound 在 scratch 输出啥?
我已经不用 scratch 很多年了。
而且为啥要在 scratch buffer 用 lsp-bridge?
跟踪到了一个 (acm-get-input-prefix) 会对这个有影响。把这个去掉,下面的代码还有个函数会有影响,但是没找到原始操作。
没想明白原因,初步推测是 org-mode 在标记任务 DONE 的时候,会自己计算定位一个位置,插入字串,并更新一些元数据,是不是 lsp-bridge 此时有对光标的操作,把当前光标位置搞乱了。所以 org 这里把对应信息插入到了不正确的地方。
只是在 Scratch中测试,在使用 lsp-bridge 的时候,其他 buffer中也会出现这个问题,不是说只是在 scratch中会出现这种问题。
其他buffer中,不知道什么时候会触发,但是在scratch中是百分百触发这个问题。
原先使用的时候没想到是lsp-bridge的问题,org 记录的时候很少看原始的文件,也是昨天发现是lsp-bridge带来的问题,暂时在org中禁用了。
我吃完饭看看吧
我用 edebug 调试(先edebug-defun 标记函数 acm-get-input-prefix-bound, 然后等 acm-get-input-prefix-bound 执行停住的时候按 d 单步执行, 进入函数内部), 得到了调用链
acm-get-input-prefix-bound()
(let ((bound (acm-get-input-prefix-bound))) (if bound (buffer-substring-no-properties (car bound) (cdr bound)) ""))
acm-get-input-prefix()
(lsp-bridge-call-file-api "change_file" lsp-bridge--before-change-begin-pos lsp-bridge--before-change-end-pos length (buffer-substring-no-properties begin end) (lsp-bridge--position) (acm-char-before) (buffer-name) (acm-get-input-prefix))
(if lsp-bridge-revert-buffer-flag nil (setq lsp-bridge-last-change-command (format "%s" this-command)) (setq lsp-bridge-last-change-position (list (current-buffer) (buffer-chars-modified-tick) (point))) (lsp-bridge-call-file-api "change_file" lsp-bridge--before-change-begin-pos lsp-bridge--before-change-end-pos length (buffer-substring-no-properties begin end) (lsp-bridge--position) (acm-char-before) (buffer-name) (acm-get-input-prefix)) (if (lsp-bridge-epc-live-p lsp-bridge-epc-process) (progn (let* ((current-word (thing-at-point 'word t)) (current-symbol (thing-at-point 'symbol t))) (if acm-enable-tabnine (progn (lsp-bridge-tabnine-complete))) (if acm-enable-search-sdcv-words (progn (if (or ... ...) nil (lsp-bridge-call-async "search_sdcv_words_search" current-word)))) (lsp-bridge-elisp-symbols-search current-symbol) (if lsp-bridge-prohibit-completion nil (if buffer-file-name (progn (let ... ...)))) (if (and (derived-mode-p 'web-mode) (acm-in-string-p) (save-excursion (search-backward-regexp "class=" ... t))) (progn (if (or ... ...) nil (lsp-bridge-call-async "search_tailwind_keywords_search" buffer-file-name current-symbol))))))))
lsp-bridge-monitor-after-change(2 8 6)
replace-match(" DONE " t t)
org-todo(nil)
funcall-interactively(org-todo nil)
call-interactively(org-todo nil nil)
command-execute(org-todo)
看样子是 acm-get-input-prefix-bound 干扰了 org-todo 内部的 replace-match 后面的代码。
修复很简单, 但是等我思考一下, 为啥这两个不相关的函数会相互干扰。
目前调试的结果是,
在 after-change-functions 函数中执行 (thing-at-point 'symbol t)
就会导致 org-todo 挂掉, 猜测应该是 org-todo 的实现不够健壮, 导致执行 (thing-at-point 'symbol t) 就会出错。