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--handler-alist nil)
;;;###autodef
(defun set-lookup-handlers! (modes &rest plist)
"Define a jump target for major MODES.
@ -31,22 +33,42 @@ Otherwise, these properties are available to be set:
:xref-backend FN
Defines an xref backend for a major-mode. If you define :definition and
: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))
(dolist (mode (doom-enlist modes))
(let ((hook (intern (format "%s-hook" mode)))
(fn (intern (format "+lookup|init-%s" mode))))
(cond ((null (car plist))
(remove-hook hook fn)
(setq +lookup--handler-alist
(delq (assq mode +lookup--handler-alist)
+lookup--handler-alist))
(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 ()
(when (or (eq major-mode mode)
(and (boundp mode)
(symbol-value mode)))
(cl-destructuring-bind
(&key definition references documentation file xref-backend)
(&key definition references documentation file xref-backend async)
plist
(when definition
(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)))))
(defun +lookup--jump-to (prop identifier &optional other-window)
(cl-loop with origin = (point-marker)
for fn
in (plist-get (list :definition +lookup-definition-functions
:references +lookup-references-functions
:documentation +lookup-documentation-functions
:file +lookup-file-functions)
prop)
for cmd = (or (command-remapping fn) fn)
if (condition-case e
(save-window-excursion
(when (or (if (commandp cmd)
(call-interactively cmd)
(funcall cmd identifier))
(/= (point-marker) origin))
(point-marker)))
(error (ignore (message "%s" e))))
return
(progn
(funcall (if other-window
#'pop-to-buffer
#'pop-to-buffer-same-window)
(marker-buffer it))
(goto-char it))))
;; TODO Refactor me
(let ((origin (point-marker)))
(cl-loop for fn
in (plist-get (list :definition +lookup-definition-functions
:references +lookup-references-functions
:documentation +lookup-documentation-functions
:file +lookup-file-functions)
prop)
for cmd = (or (command-remapping fn) fn)
if (get fn '+lookup-async)
return
(progn
(when other-window
;; If async, we can't catch the window change or destination buffer
;; reliably, so we set up the new window ahead of time.
(switch-to-buffer-other-window (current-buffer))
(goto-char (marker-position origin)))
(call-interactively fn))
if (condition-case e
(save-window-excursion
(when (or (if (commandp cmd)
(call-interactively cmd)
(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)
(unless identifier
@ -214,8 +246,7 @@ evil-mode is active."
current-prefix-arg))
(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))))
@ -231,8 +262,7 @@ search otherwise."
current-prefix-arg))
(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))))
@ -240,18 +270,15 @@ search otherwise."
(defun +lookup/documentation (identifier &optional other-window)
"Show documentation for IDENTIFIER (defaults to symbol at point or selection.
Goes down a list of possible backends:
1. The :documentation spec defined with by `set-lookup-handlers!'
2. If the +docsets flag is active for :feature lookup, use `+lookup/in-docsets'
3. Fall back to an online search, with `+lookup/online'"
First attempts the :documentation handler specified with `set-lookup-handlers!'
for the current mode/buffer (if any), then falls back to the backends in
`+lookup-documentation-functions'."
(interactive
(list (+lookup--symbol-or-region)
current-prefix-arg))
(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))))
@ -279,8 +306,7 @@ Otherwise, falls back on `find-file-at-point'."
((ffap-url-p path)
(find-file-at-point path))
((not (and +lookup-file-functions
(+lookup--jump-to :file path)))
((not (+lookup--jump-to :file path))
(let ((fullpath (expand-file-name path)))
(when (and buffer-file-name (file-equal-p fullpath buffer-file-name))
(user-error "Already here"))

View file

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