;;; syd-handle-lookup.el -*- lexical-binding: t; -*- (require 'syd-text) (use-package better-jumper) (require 'better-jumper) (defvar syd-lookup-online-documentation-backends `(("Kagi" . "https://kagi.com/search?q=%s") ("DuckDuckGo" . "https://duckduckgo.com/?q=%s") ("Nixpkgs" . "https://search.nixos.org/packages?query=%s") ("Hackage" . "https://hackage.haskell.org/packages/search?terms=%s")) "A list of pairs (NAME . BACKEND) describing the various backends `syd-lookup-online-documentation' may delegate to. NAME is a string used when speaking to the user about BACKEND. If BACKEND is an interactive command, it will be called interactively. If BACKEND is a procedure, it will be called with the search string as the lone argument. If BACKEND is a string, the user's browser will be opened to the URL returned by (format BACKEND QUERY), where QUERY is the appropriately-encoded search string.") (defvar syd-lookup-documentation-handlers '(syd-lookup-online-documentation) "A list of lookup handlers used to find documentation. A lookup handler receives an identifier, and is expected to return nil on failure, and non-nil on success. The specific return value is unused outside of the test for nil.") (defun syd-lookup--prompt-for-online-backend () (assoc-string (completing-read "Search with: " (mapcar #'car syd-lookup-online-documentation-backends) nil t) syd-lookup-online-documentation-backends)) (cl-defun syd-lookup--call-online-backend (backend &key query-string) (pcase-let ((`(,name . ,backend-fn) backend)) (cond ((functionp backend-fn) (if (commandp backend-fn) (call-interactively backend-fn) (funcall backend-fn query-string))) ((stringp backend-fn) (browse-url (format backend-fn (url-encode-url (read-string (format "Search for (on %s): " name) query-string))))) (t (signal 'wrong-type-argument `("backend" ,backend-fn)))))) ;;;###autoload (cl-defun syd-lookup-online-documentation (backend &key query-string) (interactive (list (syd-lookup--prompt-for-online-backend) :query-string (when (use-region-p) (syd-thing-at-point-or-region)))) (syd-lookup--call-online-backend (syd-lookup--prompt-for-online-backend) :query-string query-string)) ;;;###autoload (defun syd-lookup-documentation (identifier) "Try to find documentation on IDENTIFIER, and " (interactive (list (syd-thing-at-point-or-region))) (if-let ((r (syd-lookup--jump-to 'documentation identifier :display-fn #'pop-to-buffer))) r ((user-error "Couldn't find documentation on %S" (substring-no-properties identifier))))) (defvar syd-lookup--handlers-by-category '((documentation . syd-lookup-documentation-handlers))) (cl-defun syd-lookup--jump-to (category identifier &key (display-fn #'switch-to-buffer)) (let* ((handlers (alist-get category syd-lookup--handlers-by-category)) (origin (point-marker)) ;; TODO: If called with a prefix argument, prompt the user to select a ;; handler. (result (run-hook-wrapped handlers #'syd-lookup--run-handler identifier origin))) (message "result wrap hok: %S" result) (unwind-protect (when (cond ((null result) (message "No lookup handler could find %S" identifier) nil) ((markerp result) (funcall display-fn (marker-buffer result)) (goto-char result) result) (result)) (with-current-buffer (marker-buffer origin) (better-jumper-set-jump (marker-position origin))) result)) (set-marker origin nil))) (defun syd-lookup--run-handler (handler identifier origin) (condition-case-unless-debug e (let ((wconf (current-window-configuration)) (result (condition-case-unless-debug e (if (commandp handler) (call-interactively handler) (funcall handler identifier)) (error (message "Lookup handler %S threw an error: %s" handler e) 'fail)))) (message "result %S" result) (cond ((eq result 'fail) (set-window-configuration wconf) nil) ((or (get handler 'syd-lookup-async) (eq result 'deferred))) ((bufferp result) (with-current-buffer result (point-marker))) ((or result (null origin) (/= (point-marker) origin)) (prog1 (point-marker) (set-window-configuration wconf))))) ((error user-error) (message "Lookup handler %S: %s" handler e) nil))) (general-def :states 'normal "K" #'syd-lookup-documentation) (provide 'syd-handle-lookup) ;;; syd-handle-lookup.el ends here