Make set-lookup-handlers! additive

Consecutive calls to set-lookup-handlers! would redefine *all* lookup
handlers, unsetting unspecified ones, so you were forced to redefine all
handlers, even if you only wanted to change one. No more. Its side
effects are now additive.

Also adds :async handler support, however, due to their nature, they
cannot fall back to other handlers (there's no reliable way to detect
they worked or not).

To get around this, write a blocking wrapper around the old async method
and register it as a non-async handler.
This commit is contained in:
Henrik Lissner 2019-01-08 00:33:38 -05:00
parent 25b9a90c12
commit 37cb0e178c
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
2 changed files with 66 additions and 40 deletions

View file

@ -2,6 +2,8 @@
(defvar +lookup--last-provider nil) (defvar +lookup--last-provider nil)
(defvar +lookup--handler-alist nil)
;;;###autodef ;;;###autodef
(defun set-lookup-handlers! (modes &rest plist) (defun set-lookup-handlers! (modes &rest plist)
"Define a jump target for major MODES. "Define a jump target for major MODES.
@ -31,22 +33,42 @@ Otherwise, these properties are available to be set:
:xref-backend FN :xref-backend FN
Defines an xref backend for a major-mode. If you define :definition and Defines an xref backend for a major-mode. If you define :definition and
:references along with :xref-backend, those will have higher precedence. :references along with :xref-backend, those will have higher precedence.
:async BOOL
Indicates that the supplied handlers *after* this property are asynchronous.
Note: async handlers do not fall back to the default handlers, due to their
nature. To get around this, you must write specialized wrappers to wait for
the async response and return 'fallback.
\(fn MODE-OR-MODES &key DEFINITION REFERENCES DOCUMENTATION FILE XREF-BACKEND)" \(fn MODE-OR-MODES &key ASYNC DEFINITION REFERENCES DOCUMENTATION FILE XREF-BACKEND)"
(declare (indent defun)) (declare (indent defun))
(dolist (mode (doom-enlist modes)) (dolist (mode (doom-enlist modes))
(let ((hook (intern (format "%s-hook" mode))) (let ((hook (intern (format "%s-hook" mode)))
(fn (intern (format "+lookup|init-%s" mode)))) (fn (intern (format "+lookup|init-%s" mode))))
(cond ((null (car plist)) (cond ((null (car plist))
(remove-hook hook fn) (remove-hook hook fn)
(setq +lookup--handler-alist
(delq (assq mode +lookup--handler-alist)
+lookup--handler-alist))
(unintern fn nil)) (unintern fn nil))
((fset fn ((let ((old-plist (cdr (assq mode +lookup--handler-alist)))
async)
(while plist
(let ((prop (pop plist))
(fns (pop plist)))
(if (eq prop :async)
(setq async t)
(dolist (fn (doom-enlist fns))
(put fn '+lookup-async async))
(setq old-plist (plist-put old-plist prop fns)))))
(setq plist old-plist)
(setf (alist-get mode +lookup--handler-alist) plist))
(fset fn
(lambda () (lambda ()
(when (or (eq major-mode mode) (when (or (eq major-mode mode)
(and (boundp mode) (and (boundp mode)
(symbol-value mode))) (symbol-value mode)))
(cl-destructuring-bind (cl-destructuring-bind
(&key definition references documentation file xref-backend) (&key definition references documentation file xref-backend async)
plist plist
(when definition (when definition
(add-hook '+lookup-definition-functions definition nil t)) (add-hook '+lookup-definition-functions definition nil t))
@ -86,29 +108,39 @@ Otherwise, these properties are available to be set:
(xref-backend-identifier-at-point (xref-find-backend))))) (xref-backend-identifier-at-point (xref-find-backend)))))
(defun +lookup--jump-to (prop identifier &optional other-window) (defun +lookup--jump-to (prop identifier &optional other-window)
(cl-loop with origin = (point-marker) ;; TODO Refactor me
for fn (let ((origin (point-marker)))
in (plist-get (list :definition +lookup-definition-functions (cl-loop for fn
:references +lookup-references-functions in (plist-get (list :definition +lookup-definition-functions
:documentation +lookup-documentation-functions :references +lookup-references-functions
:file +lookup-file-functions) :documentation +lookup-documentation-functions
prop) :file +lookup-file-functions)
for cmd = (or (command-remapping fn) fn) prop)
if (condition-case e for cmd = (or (command-remapping fn) fn)
(save-window-excursion if (get fn '+lookup-async)
(when (or (if (commandp cmd) return
(call-interactively cmd) (progn
(funcall cmd identifier)) (when other-window
(/= (point-marker) origin)) ;; If async, we can't catch the window change or destination buffer
(point-marker))) ;; reliably, so we set up the new window ahead of time.
(error (ignore (message "%s" e)))) (switch-to-buffer-other-window (current-buffer))
return (goto-char (marker-position origin)))
(progn (call-interactively fn))
(funcall (if other-window if (condition-case e
#'pop-to-buffer (save-window-excursion
#'pop-to-buffer-same-window) (when (or (if (commandp cmd)
(marker-buffer it)) (call-interactively cmd)
(goto-char it)))) (funcall cmd identifier))
(/= (point-marker) origin))
(point-marker)))
(error (ignore (message "%s" e))))
return
(progn
(funcall (if other-window
#'switch-to-buffer-other-window
#'switch-to-buffer)
(marker-buffer it))
(goto-char it)))))
(defun +lookup--file-search (identifier) (defun +lookup--file-search (identifier)
(unless identifier (unless identifier
@ -214,8 +246,7 @@ evil-mode is active."
current-prefix-arg)) current-prefix-arg))
(cond ((null identifier) (user-error "Nothing under point")) (cond ((null identifier) (user-error "Nothing under point"))
((and +lookup-definition-functions ((+lookup--jump-to :definition identifier other-window))
(+lookup--jump-to :definition identifier other-window)))
((error "Couldn't find the definition of '%s'" identifier)))) ((error "Couldn't find the definition of '%s'" identifier))))
@ -231,8 +262,7 @@ search otherwise."
current-prefix-arg)) current-prefix-arg))
(cond ((null identifier) (user-error "Nothing under point")) (cond ((null identifier) (user-error "Nothing under point"))
((and +lookup-references-functions ((+lookup--jump-to :references identifier other-window))
(+lookup--jump-to :references identifier other-window)))
((error "Couldn't find references of '%s'" identifier)))) ((error "Couldn't find references of '%s'" identifier))))
@ -240,18 +270,15 @@ search otherwise."
(defun +lookup/documentation (identifier &optional other-window) (defun +lookup/documentation (identifier &optional other-window)
"Show documentation for IDENTIFIER (defaults to symbol at point or selection. "Show documentation for IDENTIFIER (defaults to symbol at point or selection.
Goes down a list of possible backends: First attempts the :documentation handler specified with `set-lookup-handlers!'
for the current mode/buffer (if any), then falls back to the backends in
1. The :documentation spec defined with by `set-lookup-handlers!' `+lookup-documentation-functions'."
2. If the +docsets flag is active for :feature lookup, use `+lookup/in-docsets'
3. Fall back to an online search, with `+lookup/online'"
(interactive (interactive
(list (+lookup--symbol-or-region) (list (+lookup--symbol-or-region)
current-prefix-arg)) current-prefix-arg))
(cond ((null identifier) (user-error "Nothing under point")) (cond ((null identifier) (user-error "Nothing under point"))
((and +lookup-documentation-functions ((+lookup--jump-to :documentation identifier other-window))
(+lookup--jump-to :documentation identifier other-window)))
((user-error "Couldn't find documentation for '%s'" identifier)))) ((user-error "Couldn't find documentation for '%s'" identifier))))
@ -279,8 +306,7 @@ Otherwise, falls back on `find-file-at-point'."
((ffap-url-p path) ((ffap-url-p path)
(find-file-at-point path)) (find-file-at-point path))
((not (and +lookup-file-functions ((not (+lookup--jump-to :file path))
(+lookup--jump-to :file path)))
(let ((fullpath (expand-file-name path))) (let ((fullpath (expand-file-name path)))
(when (and buffer-file-name (file-equal-p fullpath buffer-file-name)) (when (and buffer-file-name (file-equal-p fullpath buffer-file-name))
(user-error "Already here")) (user-error "Already here"))

View file

@ -148,7 +148,7 @@
(setq-default company-backends (delq 'company-tide (default-value 'company-backends)))) (setq-default company-backends (delq 'company-tide (default-value 'company-backends))))
(set-company-backend! 'tide-mode 'company-tide) (set-company-backend! 'tide-mode 'company-tide)
;; navigation ;; navigation
(set-lookup-handlers! 'tide-mode (set-lookup-handlers! 'tide-mode :async t
:definition #'tide-jump-to-definition :definition #'tide-jump-to-definition
:references #'tide-references :references #'tide-references
:documentation #'tide-documentation-at-point) :documentation #'tide-documentation-at-point)