lsp-bridge -- 速度最快的语法补全插件

发现一个问题,在 message 里面会不定期的出现下面的日志

Error running timer ‘acm-idle-completion’: (void-variable with) [2 times]

配置如下:

(add-to-list 'load-path "/Users/xqianliu/.emacs.d/.local/straight/repos/lsp-bridge")

;; lombok support
(setq lombok-path (substitute-in-file-name "$HOME/dotfiles/private/libraries/lombok.jar"))
(setq lsp-bridge-jdtls-jvm-args (format "%s%s" "-javaagent:" lombok-path))

(setq acm-fetch-candidate-doc-delay 1)
(setq acm-enable-doc nil)
(setq acm-enable-dabbrev t)
(setq lsp-bridge-enable-log t)

(require 'yasnippet)
(require 'lsp-bridge)
;; provide Java third-party library jump and -data directory support, optional
(require 'lsp-bridge-jdtls)

(yas-global-mode 1)
(global-lsp-bridge-mode)

Emacs 28.1 版本 Doom

然后正在编辑的 buffer 的名称会突然变成 .persistent-scratch 里面的语法高亮都没了,好奇怪……

前面有人说过和 persistent-scratch 冲突还是啥。没用过那个包,具体不了解。

emacs -Q 先排查一下自己的配置文件影响,再报bug

我也遇到这个问题了……

直接禁用了 persistent-scratch 这个插件,切换到了使用 doom 自带的 scratch buffer

问下你那个右下角展示按键和命令的是啥插件?

keycast

有没有方法可以在 lsp-bridge-popup-documentation 弹出文档弹窗之后,复用我常用的滚动 buffer 的快捷键,把这俩快捷键绑定到 lsp-bridge-popup-documentation-scroll-uplsp-bridge-popup-documentation-scroll-down ,这些就能很方便了浏览文档了,等关了它就恢复成之前的

你直接在acm-mode-map你加按键绑定就好了,菜单消失了按键就恢复了。

我是在关键字上用 lsp-bridge-lookup-document 弹窗的文档弹窗,不是补全项的doc

那可以弹出时新加一个mode,posframe消失时mode 设为 -1

1 个赞

继续折腾,添加了自定义 backend 的 acm-icon

;;; ~ acm-icon-advice
;;; :: A. insert my-icon into the alist
(setq acm-icon-alist (append acm-icon-alist '(("korean" "material" "korean" "#ed6856"))))
(setq acm-icon-alist (append acm-icon-alist '(("us-eng" "material" "us-eng" "#50cddc"))))
;;; :: B. add my-svg-path
(setq korean-icon-path (expand-file-name "lisp/blove-extra/blove-icons/svg" user-emacs-directory))
;;; :: C. main acm-icon-filepath-advice
(advice-add 'acm-icon-filepath
			:override (lambda (collection name)
						(if (or (string-equal name "korean") (string-equal name "us-eng"))
							(concat (file-name-as-directory korean-icon-path) (format "%s_%s.svg" collection name))
						  (concat (file-name-as-directory acm-icon-dir) (format "%s_%s.svg" collection name))
						  )
						))
;;;
;;; ~ defun candidate
(defun acm-backend-language-candidates (min-length keyword dict the-icon)
  (let* ((candidates (list)))
	(when (>= (length keyword) min-length)
	  (dolist (candidate dict)
		(when (string-prefix-p (downcase keyword) candidate)
		  (add-to-list 'candidates (list :key candidate
										 :icon the-icon
										 :label candidate
										 :display-label candidate
										 :annotation (get-text-property 0 :initials candidate)
										 :backend "blove-lang")
					   t))))

	candidates))

目前完整配置

;;;
;;; blove-lang-helper.el
;;;
;;; ~ defalias lsp-lang-toggle
(defalias 'lsp-bridge-toggle-korean-helper #'acm-toggle-korean-helper)
(defalias 'lsp-bridge-toggle-us-eng-helper #'acm-toggle-us-eng-helper)
;;;
;;; ~ defvar-local on/off
(defvar-local acm-enable-korean-helper nil)
(defvar-local acm-enable-us-eng-helper nil)
;;;
;;; ~ defcustom min-length
(defcustom acm-backend-korean-min-length 1
  "Minimum length of korean word."
  :type 'integer)
(defcustom acm-backend-us-eng-min-length 1
  "Minimum length of us english word."
  :type 'integer)
;;;
;;; ~ acm-icon-advice
;;; :: A. insert my-icon into the alist
(setq acm-icon-alist (append acm-icon-alist '(("korean" "material" "korean" "#ed6856"))))
(setq acm-icon-alist (append acm-icon-alist '(("us-eng" "material" "us-eng" "#50cddc"))))
;;; :: B. add my-svg-path
(setq korean-icon-path (expand-file-name "lisp/blove-extra/blove-icons/svg" user-emacs-directory))
;;; :: C. main acm-icon-filepath-advice
(advice-add 'acm-icon-filepath
			:override (lambda (collection name)
						(if (or (string-equal name "korean") (string-equal name "us-eng"))
							(concat (file-name-as-directory korean-icon-path) (format "%s_%s.svg" collection name))
						  (concat (file-name-as-directory acm-icon-dir) (format "%s_%s.svg" collection name))
						  )
						))
;;;
;;; ~ defun candidate
(defun acm-backend-language-candidates (min-length keyword dict the-icon)
  (let* ((candidates (list)))
	(when (>= (length keyword) min-length)
	  (dolist (candidate dict)
		(when (string-prefix-p (downcase keyword) candidate)
		  (add-to-list 'candidates (list :key candidate
										 :icon the-icon
										 :label candidate
										 :display-label candidate
										 :annotation (get-text-property 0 :initials candidate)
										 :backend "blove-lang")
					   t))))

	candidates))
;;;
;;; ~ defun lang-helper-toggle
(defun acm-toggle-korean-helper ()
  "Toggle korean helper."
  (interactive)
  (setq-local acm-enable-us-eng-helper nil)
  (setq-local the-icon "korean")
  (if acm-enable-korean-helper
	  (message "Turn off korean helper.")
	(message "Turn on korean helper."))
  (setq-local acm-enable-korean-helper (not acm-enable-korean-helper))
  )
(defun acm-toggle-us-eng-helper ()
  "Toggle us english helper."
  (interactive)
  (setq-local acm-enable-korean-helper nil)
  (setq-local the-icon "us-eng")
  (if acm-enable-us-eng-helper
	  (message "Turn off us english helper.")
	(message "Turn on us english helper."))
  (setq-local acm-enable-us-eng-helper (not acm-enable-us-eng-helper))
  )
;;;
;;; ~ add my backend ~
(add-to-list 'load-path (concat user-emacs-directory "lisp/blove-extra/blove-lang-helper/blove-lang-zh"))
(add-to-list 'load-path (concat user-emacs-directory "lisp/blove-extra/blove-lang-helper/blove-zh-lang"))
;;;
;;; ~ acm-update-candidates-advice
(defun acm-update-candidates-advice ()
  (let* ((keyword (acm-get-input-prefix))
		 (char-before-keyword (save-excursion
								(backward-char (length keyword))
								(acm-char-before)))
		 (candidates (list))
		 path-candidates
		 yas-candidates
		 tempel-candidates
		 mode-candidates)

	;; --- add language-helper for korean-helper / us-eng-helper --- begin
	(if (or acm-enable-korean-helper acm-enable-us-eng-helper)
		(progn
		  (setq-local fin-dict '())
		  (if acm-enable-korean-helper
			  (progn
				(require 'blove-backends-kor-zh-15000)
				(require 'blove-backends-zh-kor-15000)
				(setq fin-dict (append fin-dict blove-kor-zh-15000))
				(setq fin-dict (append fin-dict blove-zh-kor-15000))
				(setq candidates (acm-backend-language-candidates acm-backend-korean-min-length keyword fin-dict the-icon))
				)
			(progn
			  (if acm-enable-us-eng-helper
				  (progn
					(require 'blove-backends-us-zh-15000)
					(require 'blove-backends-zh-us-15000)
					(setq fin-dict (append fin-dict blove-us-zh-15000))
					(setq fin-dict (append fin-dict blove-zh-us-15000))
					(setq candidates (acm-backend-language-candidates acm-backend-us-eng-min-length keyword fin-dict the-icon))
					)
				)
			  )
			)
		  )
	  ;; --- add language-helper for korean-helper / us-eng-helper --- end
	  (progn
		;; from acm.el :: acm-update-candidates --- begin
		(if acm-enable-english-helper
			;; Completion english if option `acm-enable-english-helper' is enable.
			(progn
			  (require 'acm-backend-english-data)
			  (require 'acm-backend-english)

			  (setq candidates (acm-backend-english-candidates keyword)))

		  (setq path-candidates (acm-backend-path-candidates keyword))
		  (if (> (length path-candidates) 0)
			  ;; Only show path candidates if prefix is valid path.
			  (setq candidates path-candidates)

			;; Fetch syntax completion candidates.
			(setq mode-candidates (append
								   (acm-backend-elisp-candidates keyword)
								   (acm-backend-lsp-candidates keyword)))

			;; Don't search snippet if char before keyword is not in `acm-backend-lsp-completion-trigger-characters'.
			(unless (member char-before-keyword acm-backend-lsp-completion-trigger-characters)
			  (setq yas-candidates (acm-backend-yas-candidates keyword))
			  (setq tempel-candidates (acm-backend-tempel-candidates keyword)))

			;; Insert snippet candidates in first page of menu.
			(setq candidates
				  (if (> (length mode-candidates) acm-snippet-insert-index)
					  (append (cl-subseq mode-candidates 0 acm-snippet-insert-index)
							  yas-candidates
							  tempel-candidates
							  (cl-subseq mode-candidates acm-snippet-insert-index))
					(append mode-candidates yas-candidates tempel-candidates)
					))))
		;; from acm.el :: acm-update-candidates --- end
		)
	  )
	candidates))
;;; :: use acm-update-candidates-advice
(advice-add 'acm-update-candidates :override #'acm-update-candidates-advice)

;;; ========== at-the-bottom
(provide 'blove-lang-helper)

1 个赞

今天我发现如果只有一个 frame 的时候, 这个 frame 在不同的屏幕上, 补全菜单的位置都是对的。

但是如果用户在不同的屏幕上使用不同的 frame 时, acm 菜单只会在第一个 frame 上显示, 所以补全菜单的位置是错的。

已经推送了一个补丁, 在弹出补全菜单之前先检查一下 acm 菜单的 parent 和 (selected-frame) 是否一致, 如果不一致证明用户切换了不同 frame 进行操作, 遇到不一致的情况先删除 acm 菜单后新建就好了。

1 个赞

这个问题可能就是我上次遇到的

大佬切换回 lsp-bridge 吧, 这种丝滑的感觉是 lsp-mode 或 eglot 都很难给你的。

1 个赞

都有用的,主要是code action我用的还挺多的

现在 acm 可以直接在 org-mode 中使用,排除掉 lsp 和 elisp 两个后端,剩余的 yas, templet, dabbrev和english helper后端都可以在 org-mode 下直接用。

6 个赞

问下大佬,每次更新完lsp-bridge之后怎么更新状态,每次都要重启emacs太麻烦

建议重启下,因为有时候会重构,你不重启,就会导致名字在旧版和新版之间混用。

参数高亮已经完成,但是没有用 eldoc (这玩意会导致移动太快卡手), 现在采用的是 idle 方案, 迟疑 0.5 秒以后在 minibuffer 显示当前参数, 这样不影响快速输入的性能。

2 个赞

目前就剩 code action 没有实现了, 哪位大佬有兴趣, 欢迎发PR呀,我 code action 用的少。