--- This is an attempt to make company mode work for external address completion programs. We need to be able to run the address completion asynchronously. The changes are three fold: separate out the internal completion code into its own function, allow the external program to be called asynchronously, and copy the relevant code from the emacs function process-lines into our function as it doesn't appear to be available as a separate function. It seems to work in light testing but asynchronous emacs is always a little fragile/tricky. Best wishes Mark emacs/notmuch-address.el | 2 +- emacs/notmuch-company.el | 65 +++++++++++++++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 16 deletions(-) diff --git a/emacs/notmuch-address.el b/emacs/notmuch-address.el index 49e2402..65d04ce 100644 --- a/emacs/notmuch-address.el +++ b/emacs/notmuch-address.el @@ -81,7 +81,7 @@ (defcustom notmuch-address-use-company t (defun notmuch-address-setup () (let* ((use-company (and notmuch-address-use-company - (eq notmuch-address-command 'internal) + ;; (eq notmuch-address-command 'internal) (require 'company nil t))) (pair (cons notmuch-address-completion-headers-regexp (if use-company diff --git a/emacs/notmuch-company.el b/emacs/notmuch-company.el index add3161..dc3d018 100644 --- a/emacs/notmuch-company.el +++ b/emacs/notmuch-company.el @@ -42,6 +42,7 @@ (defvar company-backends) (declare-function notmuch-address-matching "notmuch-address") (defvar notmuch-address-full-harvest-finished) (defvar notmuch-address-completion-headers-regexp) +(defvar notmuch-address-command) ;;;###autoload (defun notmuch-company-setup () @@ -49,6 +50,54 @@ (defun notmuch-company-setup () (make-local-variable 'company-backends) (setq company-backends '(notmuch-company))) +(defun notmuch-company-address-internal (arg) + (cond + (notmuch-address-full-harvest-finished + ;; Update harvested addressed from time to time + (notmuch-address-harvest-trigger) + (notmuch-address-matching arg)) + (t + (cons :async + (lambda (callback) + ;; First run quick asynchronous harvest based on what the user entered so far + (notmuch-address-harvest + (format "to:%s*" arg) nil + (lambda (_proc _event) + (funcall callback (notmuch-address-matching arg)) + ;; Then start the (potentially long-running) full asynchronous harvest if necessary + (notmuch-address-harvest-trigger)))))))) + +(defun notmuch-company-external-sentinel (callback proc _event) + (let (lines) + (with-current-buffer (process-buffer proc) + ;; Copied verbatim from the process-lines function in subr.el in + ;; the standard emacs distribution. + (goto-char (point-min)) + (while (not (eobp)) + (setq lines (cons (buffer-substring-no-properties + (line-beginning-position) + (line-end-position)) + lines)) + (forward-line 1)) + (message "lines %s" lines)) + (kill-buffer (process-buffer proc)) + (funcall callback (nreverse lines)))) + +(defun notmuch-company-address-external (arg) + (cons :async + (lambda (callback) + (let* ((buf (generate-new-buffer " *notmuch-external-address*")) + (proc (start-process "notmuch-external-address" buf + notmuch-address-command arg))) + (set-process-sentinel proc (apply-partially + 'notmuch-company-external-sentinel + callback)))))) + +(defun notmuch-company-address (arg) + (if (eq notmuch-address-command 'internal) + (notmuch-company-address-internal arg) + (notmuch-company-address-external arg))) + ;;;###autoload (defun notmuch-company (command &optional arg &rest _ignore) "`company-mode' completion back-end for `notmuch'." @@ -62,21 +111,7 @@ (defun notmuch-company (command &optional arg &rest _ignore) (looking-back (concat notmuch-address-completion-headers-regexp ".*") (line-beginning-position)) (setq notmuch-company-last-prefix (company-grab "[:,][ \t]*\\(.*\\)" 1 (point-at-bol))))) - (candidates (cond - (notmuch-address-full-harvest-finished - ;; Update harvested addressed from time to time - (notmuch-address-harvest-trigger) - (notmuch-address-matching arg)) - (t - (cons :async - (lambda (callback) - ;; First run quick asynchronous harvest based on what the user entered so far - (notmuch-address-harvest - (format "to:%s*" arg) nil - (lambda (_proc _event) - (funcall callback (notmuch-address-matching arg)) - ;; Then start the (potentially long-running) full asynchronous harvest if necessary - (notmuch-address-harvest-trigger)))))))) + (candidates (notmuch-company-address arg)) (match (if (string-match notmuch-company-last-prefix arg) (match-end 0) 0)) -- 2.1.4