fix(completion/yasnippet): fix completion issues caused by yasnippet

This somewhat works around some changes to cape, which I was abusing to
effectively get a capf yasnippet function that would only return results
on exact matches. This allows normal lsp completion to work just fine,
except for exact matches against specific snippets.

Now we use the yasnippet-capf package instead, no longer needing the
company-yasnippet package, and hacking it to also only give a single
result back, with some caveats.
This commit is contained in:
2024-10-18 02:47:37 +01:00
parent d81416d0fb
commit f70de31989
3 changed files with 48 additions and 57 deletions

View File

@@ -7,6 +7,7 @@
;;; Code:
(require 'siren-company)
(require 'siren-yasnippet)
(use-package cape
:after company
@@ -14,9 +15,6 @@
(lsp-completion-mode . siren-cape-capf-lsp-mode-setup)
:preface
(defalias 'siren-yasnippet-capf
(cape-company-to-capf 'company-yasnippet))
(defun siren-cape-capf-lsp-mode-setup ()
(siren-cape-capf-add-hooks t))
@@ -25,11 +23,13 @@
;; Use `siren-prepend' function instead of `add-hook' to ensure our custom
;; completion functions are listed before `lsp-completion-at-point'.
(siren-prepend completion-at-point-functions 'cape-file)
(siren-prepend completion-at-point-functions 'siren-yasnippet-capf))
(siren-prepend completion-at-point-functions 'yasnippet-capf))
;; (siren-prepend completion-at-point-functions
;; (cape-capf-super #'lsp-completion-at-point #'yasnippet-capf))
(defun siren-cape-capf-remove-hooks (&optional local)
(remove-hook 'completion-at-point-functions 'siren-yasnippet-capf local)
(remove-hook 'completion-at-point-functions 'cape-file local))
(remove-hook 'completion-at-point-functions 'cape-file local)
(remove-hook 'completion-at-point-functions 'yasnippet-capf local))
:init
(siren-cape-capf-add-hooks))

View File

@@ -41,56 +41,5 @@
:if window-system
:hook (company-mode . company-box-mode))
(use-package company-yasnippet
:straight company
:preface
(defgroup siren-company-yasnippet nil
"Siren specific tweaks to company-yasnippet."
:group 'company)
(defcustom siren-company-yasnippet-exact-match-only nil
"Only match completion when it is a exact match for a snippet key.
This allows company-yasnippet to be used before company-capf / lsp, allowing
snippets to be easily used when exactly typing out a snippet key."
:type 'boolean
:group 'siren-company-yasnippet)
:custom
(siren-company-yasnippet-exact-match-only t)
:config
;; Dirty hack to optionally enable company-yasnippet to only match exact
;; snippet keys.
(defun company-yasnippet--completions-for-prefix (prefix key-prefix tables)
(cl-mapcan
(lambda (table)
(let ((keyhash (yas--table-hash table))
(requirement (yas--require-template-specific-condition-p))
res)
(when keyhash
(maphash
(lambda (key value)
(when (and (stringp key)
(if siren-company-yasnippet-exact-match-only
(string-equal key-prefix key)
(string-prefix-p key-prefix key)))
(maphash
(lambda (name template)
(when (yas--template-can-expand-p
(yas--template-condition template) requirement)
(push
(propertize key
'yas-annotation name
'yas-template template
'yas-prefix-offset (- (length key-prefix)
(length prefix)))
res)))
value)))
keyhash))
res))
tables)))
(provide 'siren-company)
;;; siren-company.el ends here

View File

@@ -6,6 +6,8 @@
;;; Code:
(require 'cl-lib)
(use-package yasnippet
:diminish yas-minor-mode
:hook (emacs-startup . yas-reload-all)
@@ -22,5 +24,45 @@
(when (not (file-exists-p skip-file))
(make-empty-file skip-file t))))
(use-package yasnippet-capf
:preface
(defgroup siren-yasnippet-capf nil
"Siren specific tweaks to yasnippet-capf."
:group 'yasnippet-capf)
(defcustom siren-yasnippet-capf-exact-match t
"Only return exact matches.
When non-nil, only exact matches will be returned by `yasnippet-capf'.
When nil, all matches will be returned.
Annoyingly, when enabled this seems to hide all completion results for
yasnippet, cause the capf function is run twice, once with the given
prefix, and once without any at all.
It seems like the first call is used to determine if the next capf function
should be used or not, and the second call is what company-mode uses to render
completions.
For my use-case, this is fine, as I only want `yasnippet-capf' triggered
for exact matches, and for everything else move on to the lsp-mode's
`lsp-completion-at-point' function."
:type 'boolean
:group 'siren-yasnippet-capf)
(defun siren-yasnippet-capf--advice-exact-match-only (orig-fun &rest args)
"Advice to filter out non-exact matches."
(let ((candidates (apply orig-fun args))
(prefix (nth 0 args)))
(if siren-yasnippet-capf-exact-match
(cl-remove-if-not (lambda (candidate)
(string= prefix (substring-no-properties candidate)))
candidates)
candidates)))
:config
(advice-add 'yasnippet-capf-candidates
:around #'siren-yasnippet-capf--advice-exact-match-only))
(provide 'siren-yasnippet)
;;; siren-yasnippet.el ends here